在开发database-spring-boot-starter时,添加如下可选依赖:
<!-- 数据库初始化模块 -->
<dependency>
<groupId>com.sword</groupId>
<artifactId>database-init</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
然后在自动配置类 DataBaseAutoConfig 中对database-init的组件进行注册:
/**
* 注册数据库初始化
* @return com.sword.database.service.DataBaseInitService
*/
@Bean
@ConditionalOnClass(DataBaseInitService.class)
@ConditionalOnMissingBean(DataBaseInitService.class)
public DataBaseInitService dataBaseInitService() {
return new DataBaseInitServiceImpl();
}
/**
* 注册SpringBoot容器初始化后数据库执行器
* @param dataBaseInitService 数据库初始化
* @return com.sword.database.runner.DataBaseRunner
*/
@Bean
@ConditionalOnClass(DataBaseInitRunner.class)
@ConditionalOnMissingBean(DataBaseInitRunner.class)
public DataBaseInitRunner dataBaseRunner(DataBaseInitService dataBaseInitService) {
return new DataBaseInitRunner(dataBaseInitService);
}
database-spring-boot-starter正常编译通过。因为database-spring-boot-starter中的database-init是可选依赖,所以项目依赖database-spring-boot-starter后不会间接依赖database-init,所以DataBaseInitService类肯定是不存在的,但在注册时已经标注了@ConditionalOnClass(DataBaseInitService.class),即DataBaseInitService类在classpath中不存在就不进行注册,但是项目启动时还是会报如下错误:
java.lang.IllegalStateException: Failed to introspect Class [com.sword.database.DataBaseAutoConfig] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.3.5.jar:5.3.5]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:358) ~[spring-core-5.3.5.jar:5.3.5]
at org.springframework.util.ReflectionUtils.getUniqueDeclaredMethods(ReflectionUtils.java:414) ~[spring-core-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.lambda$getTypeForFactoryMethod$2(AbstractAutowireCapableBeanFactory.java:747) ~[spring-beans-5.3.5.jar:5.3.5]
at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1688) ~[na:1.8.0_202]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryMethod(AbstractAutowireCapableBeanFactory.java:746) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:685) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:656) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.AbstractBeanFactory.isFactoryBean(AbstractBeanFactory.java:1670) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doGetBeanNamesForType(DefaultListableBeanFactory.java:570) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:542) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:667) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:659) ~[spring-beans-5.3.5.jar:5.3.5]
at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1300) ~[spring-context-5.3.5.jar:5.3.5]
at org.springframework.boot.SpringApplication.getExitCodeFromMappedException(SpringApplication.java:901) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.getExitCodeFromException(SpringApplication.java:889) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.handleExitCode(SpringApplication.java:876) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.handleRunFailure(SpringApplication.java:817) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:336) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1313) [spring-boot-2.4.4.jar:2.4.4]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) [spring-boot-2.4.4.jar:2.4.4]
at com.sword.demo.system.Application.main(Application.java:14) [test-classes/:na]
Caused by: java.lang.NoClassDefFoundError: com/sword/database/service/DataBaseInitService
at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_202]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_202]
at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_202]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.3.5.jar:5.3.5]
... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.sword.database.service.DataBaseInitService
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_202]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_202]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_202]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_202]
... 25 common frames omitted
报ClassNotFoundException原因是,虽然在扫描自动配置类 DataBaseAutoConfig 中的bean定义的时候跳过了DataBaseInitService, 但 DataBaseAutoConfig 中还有其他bean,当后续这些bean和其他bean注册有关系的时候, 会触发扫描 DataBaseAutoConfig 中的所有方法,此时 public DataBaseInitService dataBaseInitService() {} 的返回类型 DataBaseInitService 不存在,所以就会报错。
解决方式有两种,一是DataBaseInitService放在基础依赖中,然后其实现类在可选依赖中,这样DataBaseInitService类就一定能找到了;二就是单独创建一个配置类注册可选依赖的bean,这样就不会被其他不相关的bean影响到,代码如下:
/**
* 数据库初始化配置
*/
@Configuration
@ConditionalOnClass(DataBaseInitService.class)
public class DataBaseInitAutoConfig {
/**
* 注册数据库初始化
* @return com.sword.database.service.DataBaseInitService
*/
@Bean
@ConditionalOnMissingBean(DataBaseInitService.class)
public DataBaseInitService dataBaseInitService() {
return new DataBaseInitServiceImpl();
}
/**
* 注册SpringBoot容器初始化后数据库执行器
* @param dataBaseInitService 数据库初始化
* @return com.sword.database.runner.DataBaseRunner
*/
@Bean
@ConditionalOnMissingBean(DataBaseInitRunner.class)
public DataBaseInitRunner dataBaseRunner(DataBaseInitService dataBaseInitService) {
return new DataBaseInitRunner(dataBaseInitService);
}
}