多模块Spring JPA项目遭遇java.lang.ClassNotFoundException问题求助
问题描述
原本基于Spring JPA的单模块Maven测试项目运行正常,拆分为test(业务逻辑)和test-persistence(数据访问)两个模块后出现异常,删除两个模块的module-info.java后恢复正常,确认问题与Java模块声明相关。
运行时异常
Exception in thread "main" java.lang.NoClassDefFoundError: java/lang/instrument/IllegalClassFormatException at spring.data.jpa@2.7.3/org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension.getConfigurationInspectionClassLoader(JpaRepositoryConfigExtension.java:239) at spring.data.commons@2.7.3/org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport.getRepositoryConfigurations(RepositoryConfigurationExtensionSupport.java:97) at spring.data.commons@2.7.3/org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:159) at spring.data.commons@2.7.3/org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions(RepositoryBeanDefinitionRegistrarSupport.java:106) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.lambda$loadBeanDefinitionsFromRegistrars$1(ConfigurationClassBeanDefinitionReader.java:396) at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:684) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars(ConfigurationClassBeanDefinitionReader.java:395) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:157) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:129) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:343) at spring.context@5.3.23/org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247) at spring.context@5.3.23/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311) at spring.context@5.3.23/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112) at spring.context@5.3.23/org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746) at spring.context@5.3.23/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564) at spring.context@5.3.23/org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93) at test/org.test.Main.main(Main.java:15) Caused by: java.lang.ClassNotFoundException: java.lang.instrument.IllegalClassFormatException at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178) at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522) ... 17 more
调试时还会提示缺失Derby、Hibernate等类。
项目结构
- test -- src.main.java --- org.test ---- Config.java ---- Main.java --- module-info.java --- src.main.resources ---- org/test/application.properties -- pom.xml
- test-persistence -- src.main.java --- org.test.persistence ---- CustomRepository.java ---- CustomRepositoryImpl.java ---- Entity.java ---- MyRepository.java ---- PersistenceConfiguration.java --- module-info.java -- pom.xml
test模块文件
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>test</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <spring.version>5.3.23</spring.version> <spring-data.version>2.7.3</spring-data.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>test</groupId> <artifactId>test-persistence</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.23</version> </dependency> </dependencies> </project>
module-info.java
module test { requires testPersistence; requires spring.context; requires spring.beans; requires spring.core; requires spring.instrument; opens org.test; }
Config.java
package org.test; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.test.persistence.PersistenceConfiguration; @Configuration @ComponentScan(basePackages = "org.test") @Import(PersistenceConfiguration.class) public class Config { }
Main.java
package org.test; import java.util.List; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.test.persistence.Entity; import org.test.persistence.MyRepository; public class Main { public static void main(String[] args) { System.out.println("Running java " + Runtime.version().feature()); AnnotationConfigApplicationContext appCtx = new AnnotationConfigApplicationContext(Config.class); MyRepository repo = appCtx.getBean(MyRepository.class); List<Entity> result = repo.findOrderedByLastUsedLimitedTo(2); } }
application.properties
spring.jpa.generate-ddl=true spring.jpa.defer-datasource-initialization=true spring.jpa.database-platform=org.hibernate.dialect.DerbyDialect spring.jpa.hibernate.ddl-auto=update spring.datasource.driverClassName=org.apache.derby.iapi.jdbc.AutoloadedDriver spring.datasource.url=jdbc:derby:eTimer;create=true
test-persistence模块文件
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>test-persistence</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <spring.version>5.3.23</spring.version> <spring-data.version>2.7.3</spring-data.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>javax.persistence</groupId> <artifactId>javax.persistence-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring-data.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> <version>${spring.version}</version> </dependency> </dependencies> </project>
module-info.java
module testPersistence { requires java.persistence; requires java.sql; requires spring.data.commons; requires spring.data.jpa; requires spring.orm; requires spring.jdbc; requires spring.tx; requires spring.context; opens org.test.persistence; exports org.test.persistence to test; }
CustomRepository.java
package org.test.persistence; import java.util.List; import org.springframework.stereotype.Repository; @Repository public interface CustomRepository { List<Entity> findOrderedByDescriptionLimitedTo(int maxItems); }
CustomRepositoryImpl.java
package org.test.persistence; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; public class CustomRepositoryImpl implements CustomRepository { @PersistenceContext private EntityManager entityManager; @Override public List<Entity> findOrderedByDescriptionLimitedTo(int limit) { return entityManager.createQuery("SELECT p FROM Entity p ORDER BY p.description", Entity.class) .setMaxResults(limit).getResultList(); } }
Entity.java
package org.test.persistence; import javax.persistence.Column; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @javax.persistence.Entity @Table(name = "Entity") public class Entity { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String description; public Entity() { super(); } public Entity(Long id, String description) { super(); this.id = id; this.description = description; } }
MyRepository.java
package org.test.persistence; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface MyRepository extends JpaRepository<Entity, Long>, CustomRepository { Entity save(Entity task); }
PersistenceConfiguration.java
package org.test.persistence; import java.io.File; import javax.sql.DataSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; import org.springframework.transaction.PlatformTransactionManager; @Configuration @EnableJpaRepositories("org.test.persistence") @PropertySource("org/test/application.properties") public class PersistenceConfiguration { private static final String DRIVER_CLASS_NAME = "org.apache.derby.iapi.jdbc.AutoloadedDriver"; private static final String DATA_SOURCE_DRIVER_URL = "jdbc:derby:test;create=true"; private static final String DATA_SOURCE_USER = null; private static final String DATA_SOURCE_PASSWORD = null; @Bean("entityManagerFactory") public LocalContainerEntityManagerFactoryBean entityManagerFactory() { System.setProperty("derby.system.home", System.getProperty("user.home") + File.separatorChar + "test"); LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean(); entityManagerFactoryBean.setPackagesToScan("org.test"); entityManagerFactoryBean.setPersistenceUnitName("system"); entityManagerFactoryBean.setDataSource(systemDataSource()); HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter(); adapter.setDatabase(Database.DERBY); adapter.setShowSql(true); adapter.setGenerateDdl(true); entityManagerFactoryBean.setJpaVendorAdapter(adapter); return entityManagerFactoryBean; } @Bean DataSource systemDataSource() { DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName(DRIVER_CLASS_NAME); dataSource.setUrl(DATA_SOURCE_DRIVER_URL); dataSource.setUsername(DATA_SOURCE_USER); dataSource.setPassword(DATA_SOURCE_PASSWORD); return dataSource; } @Bean(name="transactionManager") public PlatformTransactionManager systemTransactionManager() { JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory((entityManagerFactory().getObject())); return transactionManager; } }
解决方案
问题核心在于Java模块系统下,依赖的模块和JDK内部模块未正确声明访问权限,导致类加载失败。
1. 修复test模块的module-info.java
添加对JDK内部模块的声明,同时补充Spring相关依赖模块:
module test { requires testPersistence; requires spring.context; requires spring.beans; requires spring.core; requires spring.instrument; requires spring.data.jpa; requires spring.orm; // 允许Spring框架代理和反射访问 opens org.test to spring.core, spring.context; // 引入JDK instrument模块 requires java.instrument; }
2. 修复test-persistence模块的module-info.java
补充缺失的依赖模块,确保Hibernate、Derby等能被正常访问:
module testPersistence { requires java.persistence; requires java.sql; requires java.instrument; requires spring.data.commons; requires spring.data.jpa; requires spring.orm; requires spring.jdbc; requires spring.tx; requires spring.context; requires spring.core; // 允许Spring和Hibernate反射访问实体类和配置类 opens org.test.persistence to spring.core, spring.context, spring.orm, spring.data.jpa; exports org.test.persistence to test; }
3. 补充缺失的依赖
- 在
test-persistence的pom.xml中添加Derby驱动依赖:
<dependency> <groupId>org.apache.derby</groupId> <artifactId>derby</artifactId> <version>10.15.2.0</version> <scope>runtime</scope> </dependency>
- 添加Hibernate核心依赖:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.14.Final</version> </dependency>
4. 调整PersistenceConfiguration
将setPackagesToScan("org.test")改为setPackagesToScan("org.test.persistence"),确保只扫描持久化模块下的实体类:
entityManagerFactoryBean.setPackagesToScan("org.test.persistence");
5. 修正Main.java中的方法名
当前代码中调用的repo.findOrderedByLastUsedLimitedTo(2)不存在,改为repo.findOrderedByDescriptionLimitedTo(2),匹配CustomRepository中的方法。
内容的提问来源于stack exchange,提问作者cattox
相关产品推荐
相关产品推荐

