Spring 源码系列(21): 自动配置原理与 AutoConfigurationImportSelector
📌 阅读前提示:第 20 篇知道
@EnableAutoConfiguration靠AutoConfigurationImportSelector批量导入配置类。本篇把这个「发动机」拆开——它从哪读配置、如何按条件过滤、如何排序,以及你的@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:
- 你的
@Bean在普通配置阶段先注册; - 自动配置类延迟导入,评估
@ConditionalOnMissingBean时发现「用户已配,跳过」; - 结果:用户 Bean 生效,自动配置退让。
📌 结论:Boot 的「约定优于配置」= 默认给你配好,但只要你显式声明同类型 Bean,就自动让位。这是最优雅的扩展点。
四、自动配置加载与过滤流程图
五、常见误区
| 误区 | 正解 |
|---|---|
| 自动配置类全部无条件注册 | 错,经@Conditional过滤,多数按需生效 |
| 你的 @Bean 一定覆盖自动配置 | 仅当自动配置类标了@ConditionalOnMissingBean同类型时 |
| Boot 2.7 还用 spring.factories 自动配置 | 兼容,但官方推荐迁移到AutoConfiguration.imports |
| DeferredImportSelector 没特殊作用 | 它让自动配置延迟导入,保证用户 Bean 优先 |
| 自动配置类在 refresh 之前就生效 | 在invokeBeanFactoryPostProcessors阶段(第5步)导入,属容器启动中 |
🧪 面试题自测
AutoConfigurationImportSelector从哪读自动配置类?- Boot 2.7 的配置清单文件与旧版
spring.factories有何不同? - 自动配置类如何按条件过滤?列举 4 个
@Conditional - 为什么用户的
@Bean能覆盖自动配置? DeferredImportSelector的作用?- 自动配置类上常见的
@ConditionalOnMissingBean解决了什么问题?
🔧 Debug 小技巧
在AutoConfigurationImportSelector.filter断点,观察configurations列表从「几百个候选」被过滤到「实际生效的几十个」;再在OnBeanCondition.getMatchOutcome断点,看@ConditionalOnMissingBean如何判定你的 Bean 已存在而跳过自动配置。
下一篇预告
第 22 篇:条件注解@Conditional全家桶——@ConditionalOnClass/@ConditionalOnMissingBean/@ConditionalOnProperty等的底层Condition实现,以及如何自定义条件注解。
如果这篇对你有帮助,欢迎点赞 · 收藏 · 关注三连支持。
Spring 源码系列共 30 篇,由浅入深持续更新中。有疑问或想深挖的源码点,评论区告诉我,下篇见。