正常情况下,在Spring boot项目中,对于多个自动配置类冲突问题,使用@EnableAutoConfiguration的exclude即可。
如下,存在两个配置类构造同一dataSource:
1.某包下DruidDataSourceAutoConfigSelf配置类(使用spring.factories进行的自动配置)
package com.example.framework.autoconfigure.druid; @Configuration @ConditionalOnClass (DruidDataSource. class ) @AutoConfigureBefore ({DataSourceAutoConfiguration. class , DruidDataSourceAutoConfigure. class }) @EnableConfigurationProperties ({DruidStatProperties. class , DataSourceProperties. class }) @Import ({DruidSpringAopConfiguration. class , DruidStatViewServletConfiguration. class , DruidWebStatFilterConfiguration. class , DruidFilterConfiguration. class }) @Slf4j public class DruidDataSourceAutoConfigSelf { @Bean (initMethod = "init" ) public DruidDataSource dataSource(Environment env) { log.info( "Init DruidDataSource" ); return DruidDataSourceBuilder .create() .build(env, "example.patch.druid." ); } } |
2.某包下CollectorDataSourceConfig配置类(使用了主类上的@ComponentScan扫描)
package com.example.common.config.datasource; @Configuration @Slf4j @ConditionalOnProperty (name = "enable.collector" , havingValue = "true" , matchIfMissing = true ) @EnableAutoConfiguration public class CollectorDataSourceConfig { ...... @Bean (name = "collectorDatasource" , initMethod = "init" , destroyMethod = "close" ) public DataSource collectorDatasource() throws Exception { DruidDataSource collectorDatasource = new DruidDataSource(); ... return collectorDatasource; } ...... } |
由于均未做MissingCondition设置,因此启动会有问题,这时仅需保留需要的即可,例如想要保留CollectorDataSourceConfig。
那,仅需按如下方式即可(DruidDataSourceAutoConfigSelf为想要移除的配置类):
@SpringBootApplication (exclude = {DruidDataSourceAutoConfigSelf. class }) @ComponentScan ({ "com.example" }) public class Application { public static void main(String[] args){ SpringApplication.run(Application. class , args); } } |
但是启动后,仍然报错,仔细观察,发现主类上存在:@ComponentScan({"com.example"})。且恰好该扫描范围囊括了上面两个配置类。有人会说,我已经排除了,为什么还会生效?由此引出本文第一个知识点:Spring Boot和Spring的机制“冲突”。
可以看到,@SpringBootApplication(exclude = {DruidDataSourceAutoConfigSelf.class})是Spring boot中的注解,而且使用了约定大于配置的“机制”(不是思想,因为这确实是一种机制)。但是,需要注意的是,他的机制是建立在Spring之上,作为”增强“而存在的,也就是他不会覆盖原来的Spring扫描机制。
所以,细心的同学可以看到,主类上的@ComponentScan({"com.example"})正好覆盖了两个配置,所以,仅仅将spring boot机制下的的自动配置排除仍不算,Spring还是会引入。
解决了这个问题后,再次启动,发现确实不先加载DruidDataSourceAutoConfigSelf(注意仅仅是先),而且还是会报同样的错,难道还有别的@ComponentScan?仔细找了一圈,没有发现,很奇怪了,继续跟代码吧。
(思路:想要排查这个问题,只能看这个类到底是被谁引入了,好在Spring有记录)
跟到配置类集合处(该map中存储了所有的配置类信息),如下:
纠结的是,我发现这个类竟然是被他的竞争对手:CollectorDataSourceConfig引入的!!!
那回到CollectorDataSourceConfig类中去看吧,果然,有一点很可疑:配置了注解@EnableAutoConfiguration(这个注解就是开头我们看到的@SpringBootApplication(exclude = {DruidDataSourceAutoConfigSelf.class}))中使用的springboot的注解,详情如下:
@Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan (excludeFilters = { @Filter (type = FilterType.CUSTOM, classes = TypeExcludeFilter. class ), @Filter (type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter. class ) }) public @interface SpringBootApplication { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor (annotation = EnableAutoConfiguration. class , attribute = "exclude" ) Class<?>[] exclude() default {}; ... } |
也就是说,费劲心机排除的设置,又被引入了。
讲到此,一目了然,说下结论吧:
- Spring boot自动配置和Spring的ComponentScan扫描会有“冲突”
- Spring boot中的注解@EnableAutoConfiguration除了主类上配置,其他地方不要填写