Spring 源码系列(21): 自动配置原理与 AutoConfigurationImportSelector

📌 阅读前提示:第 20 篇知道@EnableAutoConfigurationAutoConfigurationImportSelector批量导入配置类。本篇把这个「发动机」拆开——它从哪读配置、如何按条件过滤、如何排序,以及你的@Bean为什么能覆盖自动配置。这是 Boot 面试的「压轴题」。


一、引子:自动配置到底「自动」在哪

你没写DataSource、没写DispatcherServlet,但引入spring-boot-starter-web它们就出现了。秘密在于:AutoConfigurationImportSelector在容器启动的invokeBeanFactoryPostProcessors阶段(第 5 篇第 5 步),把一批xxxAutoConfiguration类导入容器,而这些类用@Bean+@Conditional声明了默认组件。


二、源码追踪:Selector 如何选配置

2.1 入口:DeferredImportSelector 接口

AutoConfigurationImportSelector实现DeferredImportSelector,所以它的导入延迟到所有其他配置处理完之后——保证你的@Bean先注册,自动配置后评估(这是「用户配置优先」的基础)。

2.2 读取候选配置

// AutoConfigurationImportSelector.java@OverridepublicString[]selectImports(AnnotationMetadataannotationMetadata){if(!isEnabled(annotationMetadata))returnNO_IMPORTS;AutoConfigurationEntryautoConfigurationEntry=getAutoConfigurationEntry(annotationMetadata);returnStringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}protectedAutoConfigurationEntrygetAutoConfigurationEntry(AnnotationMetadatametadata){// ① 从 META-INF/spring.factories(或 .imports)读取所有自动配置类名List<String>configurations=getCandidateConfigurations(metadata,attributes);// ② 去重configurations=removeDuplicates(configurations);// ③ 按 @AutoConfigureBefore/After/Order 排序configurations=sort(configurations,autoConfigurationMetadata);// ④ 按 exclude 属性、@Conditional 过滤configurations=filter(configurations,autoConfigurationMetadata);returnnewAutoConfigurationEntry(configurations,exclusions);}

2.3 配置从哪读(Boot 2.7 变化)

protectedList<String>getCandidateConfigurations(...){// 旧版:META-INF/spring.factories 的 EnableAutoConfiguration 键// 新版(Boot2.7+):META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsList<String>configurations=ImportCandidates.load(AutoConfiguration.class,getBeanClassLoader()).getCandidates();returnconfigurations;}

⚠️ 版本差异:Boot 2.7 起官方逐步废弃spring.factories的自动配置写法,改推AutoConfiguration.imports纯类名清单;但 2.7 仍兼容spring.factories。Boot 3 则完全移除spring.factories自动配置。

2.4 @Conditional 过滤(条件评估)

filter最终落到ConditionEvaluator.shouldSkip,评估每个xxxAutoConfiguration上的条件注解:

  • @ConditionalOnClass:classpath 存在某类才生效
  • @ConditionalOnMissingBean:容器中还没有某 Bean 才生效(这就是你自定义@Bean能覆盖它的原因)
  • @ConditionalOnProperty:配置项匹配才生效
  • @ConditionalOnWebApplication:是 Web 应用才生效
@Configuration(proxyBeanMethods=false)@ConditionalOnClass({DataSource.class,EmbeddedDatabaseType.class})@ConditionalOnMissingBean(type="io.r2dbc.spi.ConnectionFactory")// 用户没配才用自动的@EnableConfigurationProperties(DataSourceProperties.class)publicclassDataSourceAutoConfiguration{...}

三、为什么你的 @Bean 能覆盖自动配置

核心机制 =DeferredImportSelector(延迟导入)+@ConditionalOnMissingBean

  1. 你的@Bean在普通配置阶段先注册;
  2. 自动配置类延迟导入,评估@ConditionalOnMissingBean时发现「用户已配,跳过」;
  3. 结果:用户 Bean 生效,自动配置退让。

📌 结论:Boot 的「约定优于配置」= 默认给你配好,但只要你显式声明同类型 Bean,就自动让位。这是最优雅的扩展点


四、自动配置加载与过滤流程图


五、常见误区

误区正解
自动配置类全部无条件注册错,经@Conditional过滤,多数按需生效
你的 @Bean 一定覆盖自动配置仅当自动配置类标了@ConditionalOnMissingBean同类型时
Boot 2.7 还用 spring.factories 自动配置兼容,但官方推荐迁移到AutoConfiguration.imports
DeferredImportSelector 没特殊作用它让自动配置延迟导入,保证用户 Bean 优先
自动配置类在 refresh 之前就生效invokeBeanFactoryPostProcessors阶段(第5步)导入,属容器启动中

🧪 面试题自测

  1. AutoConfigurationImportSelector从哪读自动配置类?
  2. Boot 2.7 的配置清单文件与旧版spring.factories有何不同?
  3. 自动配置类如何按条件过滤?列举 4 个@Conditional
  4. 为什么用户的@Bean能覆盖自动配置?
  5. DeferredImportSelector的作用?
  6. 自动配置类上常见的@ConditionalOnMissingBean解决了什么问题?

🔧 Debug 小技巧

AutoConfigurationImportSelector.filter断点,观察configurations列表从「几百个候选」被过滤到「实际生效的几十个」;再在OnBeanCondition.getMatchOutcome断点,看@ConditionalOnMissingBean如何判定你的 Bean 已存在而跳过自动配置。


下一篇预告

第 22 篇:条件注解@Conditional全家桶——@ConditionalOnClass/@ConditionalOnMissingBean/@ConditionalOnProperty等的底层Condition实现,以及如何自定义条件注解。


如果这篇对你有帮助,欢迎点赞 · 收藏 · 关注三连支持。

Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。