正常情况下,在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.megvii"})正好覆盖了两个配置,所以,仅仅将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 { @AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude") Class<?>[] exclude() default {}; ... } |
也就是说,费劲心机排除的设置,又被引入了。
讲到此,一目了然,说下结论吧:
- Spring boot自动配置和Spring的ComponentScan扫描会有“冲突”
- Spring boot中的注解@EnableAutoConfiguration除了主类上配置,其他地方不要填写
还没有评论,来说两句吧...