正常情况下,在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 {
/**
* 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除了主类上配置,其他地方不要填写