概述
前文已分析了 @Import 与 ImportBeanDefinitionRegistrar 如何成为框架集成的万能钥匙,以及 SpringFactoriesLoader 如何从传统 SPI 演化为专用的 AutoConfiguration.imports 文件。这些机制最终汇聚于 @EnableAutoConfiguration,构成了 Spring Boot “开箱即用”的基石。本文将正面拆解自动配置的完整链路,展示 Spring Boot 如何通过 @SpringBootApplication 中的元注解,将成百上千的自动配置类按需加载到容器中。
自动配置是 Spring Boot 区别于传统 Spring 应用的核心特征。它并非某种全新的技术,而是 Spring 自身扩展点体系的一次集大成应用——通过 @Import 导入 AutoConfigurationImportSelector,利用 SpringFactoriesLoader(或 AutoConfiguration.imports)加载候选配置类,再借助条件注解进行精细化筛选,最终将满足条件的配置类注册为 BeanDefinition。整个过程严格遵循 Spring Framework 的生命周期与扩展点规范,体现了“将复杂留给框架,将简单留给开发者”的设计哲学。本文将逐层拆解这一原理,从源码层面揭示自动配置“魔法”背后的工程决策。
核心要点:
- 入口元注解:
@EnableAutoConfiguration本身只是一个标记,核心逻辑由@Import(AutoConfigurationImportSelector.class)和@Import(AutoConfigurationPackages.Registrar.class)承载。 - 候选配置加载:自动配置类列表来源于
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Boot 2.7+)和META-INF/spring.factories(传统方式),两者可共存。 - 条件筛选流程:
@ConditionalOnClass、@ConditionalOnMissingBean等条件注解在自动配置流程中扮演筛选角色,决定哪些配置类进入最终注册环节。 - 最终注册:筛选通过的配置类作为普通的
@Configuration类,由ConfigurationClassParser进一步解析,其内部的@Bean方法或额外@Import会被递归处理。 - 设计哲学:自动配置是 Spring “开闭原则”和扩展点体系的集中体现——Framework 提供容器和扩展机制,Boot 在此基础上提供预定义的配置,开发者可通过条件注解或显式声明 Bean 来覆盖默认行为。
文章组织架构图:
graph TB
subgraph s1["1.自动配置总览"]
A["SpringBootApplication 组合注解入口"] --> B["EnableAutoConfiguration"]
end
subgraph s2["2.入口拆解"]
B --> C["Import AutoConfigurationImportSelector"]
B --> D["AutoConfigurationPackage Import Registrar"]
end
subgraph s3["3.候选配置加载"]
C --> E["spring点factories 或 AutoConfiguration点imports 双重加载机制"]
end
subgraph s4["4.条件筛选"]
E --> F["条件注解过滤 ConditionEvaluator点shouldSkip"]
end
subgraph s5["5.注册与生命周期"]
F --> G["ConfigurationClassParser 解析Bean或Import"]
end
subgraph s6["6.排除与覆盖"]
G --> H["spring点autoconfigure点exclude 与 DeferredImportSelector覆盖"]
end
subgraph s7["7.配置注入助手"]
D -.-> I["EnableConfigurationProperties ImportBeanDefinitionRegistrar"]
end
subgraph s8["8.自定义实战"]
E -.-> J["自定义Starter案例"]
end
subgraph s9["9.设计哲学总结"]
C --> K["扩展点集大成"]
D --> K
E --> K
F --> K
G --> K
I --> K
end
subgraph s10["10.生产事故排查"]
H --> L["事故案例"]
F --> L
end
subgraph s11["11.面试高频"]
K --> M["含系统设计题"]
end
架构图说明:
- 总览说明:全文 11 个模块从自动配置的宏观入口出发,逐层深入加载、筛选、注册的完整链路,补充 @EnableConfigurationProperties 的机制讲解,最后通过设计哲学总结、事故排查和面试题完成闭环。
- 逐模块说明:模块 1-3 解决“有哪些自动配置类”;模块 4 解决“哪些自动配置类应该生效”;模块 5-6 解决“如何生效以及如何覆盖”;模块 7 补充配置属性注入机制;模块 8 提供实战;模块 9 提炼设计思想;模块 10-11 提供排错和面试指导。
- 关键结论:自动配置的本质是 Spring 扩展点体系的一次工程化组合,理解它需要串联 @Import、ImportSelector、ImportBeanDefinitionRegistrar、SPI 加载、条件注解和 BeanDefinition 注册等多个核心知识点。
1. 自动配置总览:从 @SpringBootApplication 到 @EnableAutoConfiguration
任何一个 Spring Boot 应用的入口类都标注了 @SpringBootApplication。这个注解是一个组合体,其内部结构直接揭示了自动配置的触发原点。
// org.springframework.boot.autoconfigure.SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 本质是@Configuration,将主类注册为配置类
@EnableAutoConfiguration // 自动配置开关
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// ...
}
其中,@SpringBootConfiguration 继承自 @Configuration,确保主类自身成为一个配置类,Spring 容器会解析其中的 @Bean 方法并执行组件扫描。@ComponentScan 负责扫描当前包及其子包下的所有组件。而 @EnableAutoConfiguration 是整个自动配置机制的唯一入口,其作用是引入 Spring Boot 预先准备好的数百个自动配置类,并根据当前 classpath 和环境条件,有选择地将它们激活。
自动配置在 Spring Boot 启动流程中的位置处于 refreshContext 的 invokeBeanFactoryPostProcessors 阶段。该阶段会触发 ConfigurationClassPostProcessor(一个 BeanDefinitionRegistryPostProcessor)对所有的配置类进行解析。@EnableAutoConfiguration 通过 @Import 导入的 AutoConfigurationImportSelector 会在此时被回调,其返回的自动配置类全限定名列表会被解析、筛选并最终注册。整个过程严格嵌入在 Spring 容器刷新周期中,与第 15 篇所述启动流程紧密衔接。
自动配置要解决的核心问题是:如何让开发者引入一个 Starter 依赖后,无需任何手动配置即可自动获得对应的 Bean。例如,引入 spring-boot-starter-data-redis 后,RedisTemplate 和 RedisConnectionFactory 会被自动创建;引入 spring-boot-starter-web 后,DispatcherServlet 和内嵌 Tomcat 被自动装配。这一切并非魔法,而是依赖于自动配置类在背后的精确判断与装配。
2. 入口拆解:@EnableAutoConfiguration 注解内部的 Import 链
@EnableAutoConfiguration 的源码非常精简,却承载了两个至关重要的 Import 动作:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // ①
@Import(AutoConfigurationImportSelector.class) // ②
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
- ①
@AutoConfigurationPackage:利用@Import(AutoConfigurationPackages.Registrar.class)记录自动配置的扫描基准包,主要用于内部框架(如 Spring Data JPA 的实体扫描)确定包范围。 - ②
@Import(AutoConfigurationImportSelector.class):自动配置的绝对核心,负责加载并筛选所有候选自动配置类。
2.1 @AutoConfigurationPackage:ImportBeanDefinitionRegistrar 的又一应用
@AutoConfigurationPackage 注解本身也仅作为标记存在,其真正的逻辑在 AutoConfigurationPackages.Registrar 中:
// org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
Registrar 实现了 ImportBeanDefinitionRegistrar,这正是第 10 篇深入讲解的 Spring 扩展点。registerBeanDefinitions 方法会获取注解所在类的包名(即主类所在包),然后将其注册为一个名为 AutoConfigurationPackages 的内部 Bean。这个 Bean 供后续其他组件(例如 JpaRepositoriesAutoConfiguration)通过 AutoConfigurationPackages.get(beanFactory) 获取到基础扫描包,从而确定实体扫描范围。这再次证明,Spring Boot 内部的许多机制都是基于 ImportBeanDefinitionRegistrar 实现的。
2.2 AutoConfigurationImportSelector 的继承体系
classDiagram
class ImportSelector {
<<interface>>
+selectImports(AnnotationMetadata) String[]
}
class DeferredImportSelector {
+getImportGroup() Class~Group~
}
class AutoConfigurationImportSelector {
+selectImports(AnnotationMetadata) String[]
+getAutoConfigurationEntry(...) AutoConfigurationEntry
-getCandidateConfigurations(...) List~String~
-filter(...) List~String~
+getExclusions(...) Set~String~
}
ImportSelector <|-- DeferredImportSelector
DeferredImportSelector <|-- AutoConfigurationImportSelector
AutoConfigurationImportSelector ..> ConfigurationClassParser : 被回调
AutoConfigurationImportSelector ..> SpringFactoriesLoader : 加载候选
AutoConfigurationImportSelector ..> FilteringSpringBootCondition : 条件筛选
图表主旨概括:展示 AutoConfigurationImportSelector 如何通过实现 DeferredImportSelector 成为特殊的 ImportSelector,并关联其核心方法。
逐层/逐元素分解:
ImportSelector是 Spring Framework 提供的 SPI,允许根据注解元数据动态返回需要导入的类名。DeferredImportSelector是ImportSelector的子接口,表示该选择器的执行时机被推迟到所有常规配置类处理完毕之后,并支持分组排序。AutoConfigurationImportSelector实现了DeferredImportSelector,并覆写了selectImports、getAutoConfigurationEntry等核心方法,集成了加载、排除、过滤等全套逻辑。
设计原理映射:这里使用了模板方法模式:selectImports 提供了算法骨架,具体步骤(获取候选、排除、过滤)由子步骤方法完成。同时,DeferredImportSelector 本身是回调机制与优先级控制的应用,通过延迟加载保障用户自定义 Bean 的优先性。
工程联系与关键结论:AutoConfigurationImportSelector 之所以必须实现 DeferredImportSelector,是因为自动配置 Bean 应当作为默认行为存在,用户显式定义的 Bean 必须能够覆盖它们。延迟处理保证了所有用户配置类先被解析,自动配置类随后加载,配合 @ConditionalOnMissingBean 即可实现“用户优先”策略。
3. 候选配置的加载:AutoConfigurationImportSelector 与双配置文件
自动配置最关键的步骤之一就是确定“有哪些候选的自动配置类”。Spring Boot 从 2.7 版本开始,引导开发者从传统的 spring.factories 文件迁移到专用的 AutoConfiguration.imports 文件,同时保持对旧方式的兼容。
3.1 selectImports 与 getAutoConfigurationEntry
当 ConfigurationClassParser 处理到 @EnableAutoConfiguration 的 @Import 时,会调用 AutoConfigurationImportSelector.selectImports,其内部委托给 getAutoConfigurationEntry:
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 1. 加载所有候选自动配置类名称
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 2. 去重
configurations = removeDuplicates(configurations);
// 3. 获取显式要排除的类(来自属性或注解)
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 4. 通过 AutoConfigurationImportFilter 进行第一轮过滤(条件注解快速筛选)
configurations = getConfigurationClassFilter().filter(configurations);
// 5. 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 6. 构建结果
return new AutoConfigurationEntry(configurations, exclusions);
}
这个骨架清晰展示了自动配置的流水线:
- 加载候选列表 -> 2. 移除显式排除项 -> 3. 条件过滤器快速筛选 -> 4. 发布事件 -> 5. 返回最终配置类数组。
3.2 候选配置的双重加载机制
getCandidateConfigurations 方法内部决定从何处读取候选类名单:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 新方式:从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 加载
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. "
+ "If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
在 Spring Boot 2.7.x 中,默认已经优先使用 ImportCandidates.load 从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中读取。但传统 spring.factories 的机制并未完全移除,Spring Boot 内部通过兼容层依然可以处理旧式 Starter 提供的 EnableAutoConfiguration key。
ImportCandidates.load 本质上是读取类路径下指定位置的资源文件,每行即为一个候选自动配置类的全限定名。新的文件格式去除了 key=value 的结构,更加纯粹和高效。
SpringFactoriesLoader.loadFactoryNames 的传统路径也在兼容过程中被调用,具体取决于 SpringFactoriesLoader$FactoryProvider 等内部实现。对于采用 Spring Boot 2.7.x 的纯新工程,推荐使用 AutoConfiguration.imports 文件。
sequenceDiagram
participant Selector as AutoConfigurationImportSelector
participant IC as ImportCandidates
participant SFL as SpringFactoriesLoader
participant RES as 类路径资源
Selector->>Selector: getCandidateConfigurations()
alt 新机制 (Boot 2.7+)
Selector->>IC: load(AutoConfiguration.class)
IC->>RES: 读取 META-INF/spring/...AutoConfiguration.imports
RES-->>IC: 候选类名列表
else 传统机制 (兼容)
Selector->>SFL: loadFactoryNames(EnableAutoConfiguration.class)
SFL->>RES: 读取 META-INF/spring.factories
RES-->>SFL: 候选类名列表
end
Selector-->>Selector: 合并去重,返回全部候选
图表主旨概括:展示 AutoConfigurationImportSelector 加载候选配置时的新旧文件判断与读取流程。
逐层/逐元素分解:
AutoConfigurationImportSelector作为客户端,调用ImportCandidates.load或SpringFactoriesLoader.loadFactoryNames来获取配置类名称。ImportCandidates.load从专用的AutoConfiguration.imports文件中读取,格式为纯文本列表。SpringFactoriesLoader.loadFactoryNames从spring.factories中读取 key 为org.springframework.boot.autoconfigure.EnableAutoConfiguration的列表。
设计原理映射:这是策略模式与适配器模式的结合。两种加载方式最终产出统一的 List<String>,调用方无需关心底层文件格式。新方式的引入降低了配置的复杂性,同时保证了向前兼容。
工程联系与关键结论:Spring Boot 2.7.x 同时支持两种配置文件,但官方推荐迁移至 AutoConfiguration.imports。该文件简洁、专一,避免了 spring.factories 中 key 冲突和人为格式化错误的风险,是 Spring Boot 不断消除旧有 SPI 痕迹、走向专用化配置的重要一步。
4. 条件筛选:自动配置流程中的过滤器角色
有了候选配置类列表后,必须判断在当前应用中每个配置类是否应该生效。这依靠条件注解(@Conditional 系列)进行筛选。条件注解的完整实现细节将在后续《条件装配全家桶》专篇深入,本章聚焦其在自动配置流程中的定位和调用链路。
4.1 条件筛选的入口:filter 方法
在 getAutoConfigurationEntry 中,调用了 getConfigurationClassFilter().filter(configurations)。这个 ConfigurationClassFilter 包装了多个 AutoConfigurationImportFilter 实现。这些过滤器在自动配置层面提前评估 @ConditionalOnClass、@ConditionalOnBean 等条件,快速排除不满足条件的类,避免它们进入 ConfigurationClassParser 的完整解析,从而提升启动性能。
核心过滤器实现包含:
OnClassCondition(处理@ConditionalOnClass/@ConditionalOnMissingClass)OnBeanCondition(处理@ConditionalOnBean/@ConditionalOnMissingBean)OnWebApplicationCondition(处理@ConditionalOnWebApplication)
4.2 ConditionEvaluator.shouldSkip:Spring Framework 层面的判断
自动配置类即使通过快速过滤,后续在 ConfigurationClassParser 处理时,仍会由 Spring Framework 的标准条件评估机制再次校验。ConditionEvaluator.shouldSkip 是这一机制的核心入口:
// org.springframework.context.annotation.ConditionEvaluator
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationCondition.ConfigurationPhase phase) {
// 如果类或方法上没有@Conditional注解,直接返回 false(不应跳过)
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
// 获取所有@Conditional注解,实例化对应的Condition对象并逐一评估
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
for (Condition condition : conditions) {
ConfigurationCondition.ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
// 如果阶段不匹配,则跳过评估(例如,BEAN_REGISTRATION阶段的Condition在PARSE_CONFIGURATION时忽略)
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(this.context, metadata)) {
return true; // 任一条件不匹配,则整个配置类或Bean方法应被跳过
}
}
}
return false;
}
这段代码表明:条件评估发生在 ConfigurationClassParser 处理每一个配置类或 @Bean 方法时。它与 ConfigurationClassPostProcessor(BDRPP)紧密关联——ConfigurationClassPostProcessor 触发解析,ConfigurationClassParser 逐步解析每个配置类,解析前调用 shouldSkip 判断该类是否应被忽略。
自动配置类上的条件注解如 @ConditionalOnClass、@ConditionalOnMissingBean 等最终都会被转化为 Condition 实现类,在此处被评估。
sequenceDiagram
participant Selector as AutoConfigurationImportSelector
participant Filter as ConfigurationClassFilter
participant FBCond as FilteringSpringBootCondition
participant CondEval as ConditionEvaluator
participant Parser as ConfigurationClassParser
Selector->>Filter: filter(configurations)
loop 对每个候选配置类
Filter->>FBCond: match(metadata)
FBCond->>FBCond: 评估条件(OnClass等)
alt 不满足
FBCond-->>Filter: false(移除此类)
else 满足
FBCond-->>Filter: true
end
end
Filter-->>Selector: 初步筛选后的列表
Selector-->>Parser: 返回最终类名数组
Parser->>Parser: processConfigurationClass()
Parser->>CondEval: shouldSkip(metadata)
CondEval->>CondEval: 执行所有Condition.matches
alt 任一条件失败
CondEval-->>Parser: true(跳过)
else 全部满足
CondEval-->>Parser: false(继续解析)
end
图表主旨概括:说明自动配置的条件筛选分为两级——第一级在 AutoConfigurationImportSelector 层面的快速过滤,第二级在 ConfigurationClassParser 解析配置类时的标准条件评估。
逐层/逐元素分解:
ConfigurationClassFilter持有多个FilteringSpringBootCondition子类,专门针对自动配置场景做早期过滤,减少不必要解析。ConditionEvaluator.shouldSkip是 Spring Framework 的标准条件检查入口,保证配置类或 Bean 方法在任何阶段都受到 @Conditional 约束。ConfigurationClassParser在每条配置类处理前都会调用shouldSkip,从而与自动配置的过滤结果形成双重保障。
设计原理映射:两层过滤体现了性能优化与职责分离。早期快速过滤避免了加载和解析无用的配置类,标准条件评估则保证了框架行为的统一性和可扩展性。这可以看作装饰器模式和责任链模式的组合。
工程联系与关键结论:理解条件评估的时机对于解决自动配置不生效的问题至关重要。若某个自动配置类在第一级过滤被移除(比如缺少某个 Class),即便后期 classpath 满足条件,该类也不会再生效。而 shouldSkip 在解析阶段执行,也解释了为何某些条件必须在 BeanDefinition 注册前完成判断,开发者自定义 Condition 时需要选择合适的评估阶段(ConfigurationPhase)。
5. 筛选后配置类的注册与生命周期
AutoConfigurationImportSelector.selectImports 最终返回一个字符串数组,这些字符串就是将要导入的自动配置类全限定名。ConfigurationClassParser 在处理 @Import 时,收到这些类名并逐个解析:
// org.springframework.context.annotation.ConfigurationClassParser
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
// ...
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// ...处理 ImportSelector
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// ...处理 Registrar
}
else {
// 普通配置类:递归处理
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
自动配置类会被当作普通的配置类(candidate.asConfigClass(configClass)),进入 processConfigurationClass 解析。解析流程包括:
- 将其封装为
ConfigurationClass。 - 递归处理该类上可能存在的额外
@Import、@ComponentScan等注解。 - 解析类内部所有带有
@Bean注解的方法,为每个方法生成一个BeanMethod并关联到ConfigurationClass。 - 最终,所有这些
ConfigurationClass会被ConfigurationClassBeanDefinitionReader加载,将其中的@Bean方法转化为BeanDefinition并注册到容器。
自动配置类中的 @Bean 方法默认使用 Full 模式(@Configuration(proxyBeanMethods = true)),这意味着这些方法会被 CGLIB 代理增强,以确保容器内单例 Bean 的正确引用,这与前文 Bean 生命周期篇章所述一致。
sequenceDiagram
participant Parser as ConfigurationClassParser
participant AutoConfig as 自动配置类 (如 DataSourceAutoConfiguration)
participant Reader as ConfigurationClassBeanDefinitionReader
participant Registry as BeanDefinitionRegistry
Parser->>Parser: processImports(类名数组)
Parser->>AutoConfig: processConfigurationClass()
activate AutoConfig
AutoConfig->>AutoConfig: 处理类上注解(@Import, @ComponentScan)
AutoConfig->>AutoConfig: 收集@Bean方法
AutoConfig-->>Parser: ConfigurationClass
deactivate AutoConfig
Parser-->>Reader: 提交所有ConfigurationClass
Reader->>Reader: loadBeanDefinitions()
loop 每个@Bean方法
Reader->>Registry: registerBeanDefinition(beanName, beanDef)
end
图表主旨概括:展示筛选通过的自动配置类如何经由 ConfigurationClassParser 解析并最终将内部的 Bean 定义注册到容器中。
逐层/逐元素分解:
ConfigurationClassParser.processImports处理AutoConfigurationImportSelector返回的字符串类名,将其作为普通配置类递归解析。- 自动配置类中的
@Bean方法被识别、建模为BeanMethod,交给ConfigurationClassBeanDefinitionReader。 ConfigurationClassBeanDefinitionReader遍历每个BeanMethod,为其创建BeanDefinition并调用registry.registerBeanDefinition完成注册。
设计原理映射:这是组合模式和编译器模式的体现——配置类被解析为语法树节点,@Bean 方法作为叶节点,最终生成目标定义。Spring 容器本身相当于一个编译器,将配置元数据编译为可运行的 Bean 定义。
工程联系与关键结论:自动配置类与开发者手动编写的 @Configuration 类在容器看来没有任何区别,完全遵循相同的解析和注册逻辑。这意味着开发者可以完全使用 Spring Framework 提供的标准扩展点(如 BeanPostProcessor)干预或增强自动配置产生的 Bean,实现无缝融合。
6. 自动配置的排除与覆盖:spring.autoconfigure.exclude 与 Bean 定义优先级
6.1 禁用特定的自动配置类
Spring Boot 提供两种方式排除某些自动配置类:
- 属性配置:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration - 注解参数:
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
AutoConfigurationImportSelector.getExclusions 方法会收集这两种来源的排除项,并在 getAutoConfigurationEntry 中将它们从候选列表里移除。
// 内部通过属性绑定和注解解析获取排除类名
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
排除发生在条件过滤之前,因此被排除的类甚至不会进入条件评估阶段。
6.2 自动配置 Bean 的覆盖机制
Spring Boot 自动配置输出的 Bean 大多是“默认行为”,设计上必须允许开发者覆盖。这一目标通过两个核心机制实现:
DeferredImportSelector延迟加载:自动配置类被推迟到用户所有配置类解析完成之后再处理。这意味着用户使用@Bean定义的 Bean 会先于自动配置的@Bean生成并注册到容器中。@ConditionalOnMissingBean条件保护:自动配置类中的 Bean 定义通常使用该注解,当容器中已存在同类型 Bean 时,当前自动配置片段被跳过。
例如,DataSourceAutoConfiguration 的内部类 PooledDataSourceConfiguration 中:
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource dataSource() {
// 创建默认的 HikariCP 数据源
}
如果开发者在自己的配置类中定义了一个 DataSource Bean,容器在解析自动配置时发现该 Bean 已存在,@ConditionalOnMissingBean 条件不满足,自动配置的数据源便被跳过。这就是“用户定义的 Bean 优先”的根本实现。
7. @EnableConfigurationProperties:自动配置的“配置注入”助手
外部化配置是 Spring Boot 的另一大核心特性。@EnableConfigurationProperties 充当了将 application.properties/yml 中的配置值绑定到 POJO 并注册为 Bean 的关键桥梁。
// org.springframework.boot.context.properties.EnableConfigurationProperties
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
它同样使用了 @Import,引入 EnableConfigurationPropertiesRegistrar,而后者正是 ImportBeanDefinitionRegistrar 的实现类:
// org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar
class EnableConfigurationPropertiesRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 将@EnableConfigurationProperties中指定的所有类以 BeanDefinition 注册到容器
registerInfrastructureBeans(registry);
ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
getTypes(metadata).forEach(beanRegistrar::register);
}
}
自动配置类经常配合 @EnableConfigurationProperties 使用,例如:
@AutoConfiguration
@EnableConfigurationProperties(SomeServiceProperties.class)
public class SomeServiceAutoConfiguration {
private final SomeServiceProperties properties;
public SomeServiceAutoConfiguration(SomeServiceProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService(properties.getConfigValue());
}
}
这样,SomeServiceProperties 被提前注册为 Bean 并且完成了属性绑定,自动配置类可以直接通过构造器注入使用。
sequenceDiagram
participant Parser as ConfigurationClassParser
participant Registrar as EnableConfigurationPropertiesRegistrar
participant Registry as BeanDefinitionRegistry
participant AutoConfig as XxxAutoConfiguration
Parser->>Parser: 处理@Import(EnableConfigurationPropertiesRegistrar)
Parser->>Registrar: registerBeanDefinitions(metadata, registry)
Registrar->>Registry: registerBeanDefinition("xxxProperties", beanDef)
Note over Registry: 注册属性类为BeanDefinition<br>并标记为绑定候选
Parser->>AutoConfig: 解析自动配置类
AutoConfig-->>AutoConfig: 构造注入SomeServiceProperties
AutoConfig->>Registry: 注册SomeService Bean
图表主旨概括:展示 @EnableConfigurationProperties 如何通过 ImportBeanDefinitionRegistrar 提前注册属性 Bean,以便自动配置类使用。
逐层/逐元素分解:
- Parser 处理自动配置类上附加的
@Import(EnableConfigurationPropertiesRegistrar.class)。 Registrar执行registerBeanDefinitions,将指定的@ConfigurationProperties类注册为容器 Bean。- 随后的自动配置类便可以通过普通依赖注入获取到该属性 Bean,实现配置解耦。
设计原理映射:@EnableConfigurationProperties 与 @AutoConfigurationPackage 一样,都是基于 ImportBeanDefinitionRegistrar 的扩展点应用。它们将特定 Bean 的注册逻辑封装起来,使得自动配置类仅需声明即可支持配置注入和包信息记录。这是门面模式与声明式编程的结合。
工程联系与关键结论:Spring Boot 自动配置中,@EnableConfigurationProperties 是连接外部化配置与自动装配的纽带。它的实现再次证实 Spring Boot 内部大量复用 @Import 和 ImportBeanDefinitionRegistrar 扩展点,保持了体系结构的统一与清晰。理解这一机制对排查配置绑定失效问题极有帮助。
8. 自定义自动配置实战
以下通过创建一个“Greeter Starter”展示完整的自动配置流程。
8.1 Starter 模块结构
my-greeter-spring-boot-starter
├── pom.xml
└── src/main/java/com/example/greeter
├── GreeterService.java
├── GreeterProperties.java
├── GreeterAutoConfiguration.java
└── src/main/resources/META-INF/spring
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
8.2 源代码实现
// GreeterProperties.java – 绑定配置属性 greeter.prefix 和 greeter.suffix
@ConfigurationProperties(prefix = "greeter")
public class GreeterProperties {
private String prefix = "Hello, ";
private String suffix = "!";
// getters & setters...
}
// GreeterService.java – 使用属性拼接欢迎语
public class GreeterService {
private final GreeterProperties properties;
public GreeterService(GreeterProperties properties) {
this.properties = properties;
}
public String greet(String name) {
return properties.getPrefix() + name + properties.getSuffix();
}
}
// GreeterAutoConfiguration.java – 自动配置类
@AutoConfiguration
@EnableConfigurationProperties(GreeterProperties.class)
public class GreeterAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户可自定义GreeterService覆盖
public GreeterService greeterService(GreeterProperties properties) {
return new GreeterService(properties);
}
}
AutoConfiguration.imports 文件内容(需放置于 META-INF/spring/ 目录下):
com.example.greeter.GreeterAutoConfiguration
8.3 验证测试
在测试项目中引入 starter:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(TestApplication.class, args);
GreeterService service = ctx.getBean(GreeterService.class);
System.out.println(service.greet("World")); // 输出: Hello, World!
}
}
若在测试项目中自定义一个 GreeterService Bean:
@Configuration
public class CustomConfig {
@Bean
public GreeterService customGreeter() {
return name -> "Hi, " + name + "!";
}
}
此时自动配置的 GreeterService 因 @ConditionalOnMissingBean 存在而被跳过,最终容器中的唯一输出变为 Hi, World!。这完美演示了自动配置的覆盖机制。
9. 自动配置的设计哲学与扩展点总结
将自动配置与前文所学的 Spring 扩展点进行系统性对照:
| 扩展点机制 | 在自动配置中的应用 | 对应注解 / 类 |
|---|---|---|
@Import + ImportSelector | 动态加载全部候选自动配置类 | AutoConfigurationImportSelector |
@Import + ImportBeanDefinitionRegistrar | 记录基准扫描包 | @AutoConfigurationPackage (Registrar) |
@Import + ImportBeanDefinitionRegistrar | 注册属性配置 Bean | @EnableConfigurationProperties (Registrar) |
SpringFactoriesLoader / AutoConfiguration.imports | SPI 发现机制,加载候选配置列表 | spring.factories 或专用文件 |
@Conditional 及派生注解 | 按当前环境过滤配置类 | @ConditionalOnClass、@ConditionalOnMissingBean 等 |
@Configuration + @Bean | 定义条件满足时应创建的 Bean | 自动配置类内部 |
DeferredImportSelector | 延迟加载,保障用户 Bean 优先 | AutoConfigurationImportSelector |
自动配置的设计哲学可归纳为:
- 约定优于配置:框架提供了一套默认行为,开发者只需引入依赖即可工作,无需编写配置。
- 显式覆盖:任何时候开发者都可以通过显式定义 Bean 或修改属性来覆盖默认行为,框架不强制。
- 按需加载:仅在 classpath 存在特定类或环境满足条件时,对应的自动配置才被激活,避免不必要的资源消耗。
- 复用标准扩展点:自动配置并非 Spring Boot 独创技术,而是完全构建在 Spring Framework 的
@Import、条件注解、SPI 等成熟扩展点之上,保持了体系的一致性。
自动配置是 Spring 可扩展性在应用层的集中兑现。它证明了,只要掌握了容器的一系列 SPI,就可以创造出极具生产力的二次封装框架,而不必触碰核心容器代码。
10. 生产事故排查专题
事故一:引入 Redis Starter 后 RedisTemplate 未注册
现象:Spring Boot 应用添加了 spring-boot-starter-data-redis 依赖,但运行时报错 No qualifying bean of type 'org.springframework.data.redis.core.RedisTemplate'.
排查思路:
- 启动时添加
--debug参数,查看CONDITIONS EVALUATION REPORT。 - 在报告中搜索
RedisAutoConfiguration,发现其匹配状态为Did not match,原因是@ConditionalOnClass条件失败,具体埋点类org.springframework.data.redis.core.RedisOperations未找到。
根因:项目依赖管理中引入了 spring-data-redis,但 Maven 传递依赖因冲突被排除,导致 RedisOperations 类缺失。自动配置类上的 @ConditionalOnClass({RedisOperations.class}) 判定类不存在,因此整个 RedisAutoConfiguration 被跳过,RedisTemplate 未曾注册。
解决:修复依赖冲突,确保 spring-data-redis 完整地进入 classpath。重启后自动配置成功。
最佳实践:始终开启启动 debug 报告排查自动配置问题,避免凭猜测替换依赖。
事故二:自定义 DataSource 自动配置被 Hikari 默认覆盖
现象:公司内部编写了一个自定义的 DataSourceAutoConfiguration(使用 DBCP2),并排除了官方的 DataSourceAutoConfiguration。但在某些环境发现连接的池仍是 HikariCP。
排查思路:
- 检查启动日志,发现
DataSourceAutoConfiguration被跳过,但存在另一个DataSourceConfiguration被匹配。 - 查看
AutoConfiguration.imports文件,发现官方 starter 中定义了多个数据源配置内嵌类,如DataSourceConfiguration.Hikari,它们都受@ConditionalOnClass和@ConditionalOnMissingBean条件约束。 - 由于自定义配置中 Bean 的定义名称与 Hikari 配置的 Bean 名称不同,
@ConditionalOnMissingBean无法覆盖,导致两个数据源同时注册,但 Hikari 的@Primary等机制导致最终注入的是 Hikari DataSource。
根因:自定义数据源 Bean 被命名为 customDataSource,而 Hikari 自动配置生成的 Bean 名称为 dataSource。容器中存在两个 DataSource,且 Hikari 配置可能使用了 @Primary,导致注入时选择了默认 Bean。对 @ConditionalOnMissingBean 理解不足,未考虑 Bean 名称匹配逻辑。
解决:修改自定义配置,统一使用 @Bean(name = "dataSource") 并通过 @Primary 或排除 Hikari 子配置类彻底解决。
最佳实践:深入理解 @ConditionalOnMissingBean 的匹配粒度(类型+名称),覆盖默认 Bean 时必须保持一致的类型和名称;或者使用 @AutoConfigureBefore 调整配置顺序。
11. 面试高频专题
1. @EnableAutoConfiguration 是如何工作的?
@EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector.class) 触发自动配置。AutoConfigurationImportSelector 实现了 DeferredImportSelector,在容器启动的 invokeBeanFactoryPostProcessors 阶段被回调。它使用 SpringFactoriesLoader(或 AutoConfiguration.imports 文件)加载所有候选自动配置类,然后根据条件注解筛选出满足环境要求的类,最终将这些类交由 ConfigurationClassParser 解析并注册为 Bean。
- 追问:
DeferredImportSelector与普通ImportSelector的区别是什么?延迟执行,支持分组排序,保证用户 Bean 优先。 - 追问:如果有两个 Starter 都自动配置了相同的 Bean,如何决定加载顺序?使用
@AutoConfigureBefore/@AutoConfigureAfter和AutoConfigurationImportSelector的排序机制。 - 追问:自动配置类的 spring.factories 和 AutoConfiguration.imports 优先级如何?Boot 2.7+ 优先使用 .imports 文件,但它内部会合并两者而不会完全忽略 spring.factories。
2. @AutoConfigurationPackage 的作用是什么?它是如何实现的?
@AutoConfigurationPackage 用于记录自动配置所在的基准包。内部通过 @Import(AutoConfigurationPackages.Registrar.class) 实现,Registrar 是 ImportBeanDefinitionRegistrar 的实现,在 registerBeanDefinitions 阶段将注解所在包名注册为一个名为 AutoConfigurationPackages 的 Bean。后续组件(如 JPA 实体扫描)通过该类获取基础包。
- 追问:为什么需要这个基础包信息?用于自动扫描实体、组件等,避免在无
@ComponentScan的自动配置环境中缺少扫描依据。 - 追问:可以手动指定其他包吗?可通过
@AutoConfigurationPackage(basePackages = "...")修改,但通常不推荐,会影响默认行为。 - 追问:这个 Bean 在容器中如何被查询?
AutoConfigurationPackages.get(beanFactory)静态方法获取。
3. 自动配置类是通过什么机制被加载和筛选的?
加载机制是 SPI 式发现:传统方式通过 META-INF/spring.factories 中的 org.springframework.boot.autoconfigure.EnableAutoConfiguration key 读取;新方式(Boot 2.7+)通过 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件逐行读取全限定类名。筛选机制分两层:第一层在 AutoConfigurationImportSelector 内部使用 ConfigurationClassFilter 快速过滤;第二层在 ConfigurationClassParser 解析时使用 ConditionEvaluator.shouldSkip 标准评估。
- 追问:如果同时在两个文件中定义,类是否会重复?不会,内部会去重。
- 追问:筛选过程中如何判断
@ConditionalOnClass?通过反射尝试加载类,若抛出ClassNotFoundException则条件不满足。 - 追问:
FilteringSpringBootCondition与标准Condition有何不同?它们专门针对自动配置优化,支持提前过滤,减少容器启动负担。
4. @ConditionalOnMissingBean 在自动配置中扮演什么角色?
它充当“保护伞”,确保只在用户未提供相同 Bean 时才创建自动配置的默认 Bean。这是实现“用户覆盖默认行为”的关键手段。该注解在自动配置类中广泛使用。
- 追问:如果用户定义了一个父类型,自动配置定义了子类型,会怎样?
@ConditionalOnMissingBean默认匹配类型,但不包含父子层级关系(除非指定search = SearchStrategy.ALL等),可能导致自动配置的子 Bean 依然被创建。 - 追问:名称不匹配但类型相同,会自动覆盖吗?默认根据 Bean 类型匹配,不关心名称,因此类型相同即判定已存在,自动配置跳过。
- 追问:定义在
@Bean方法和配置类上的区别是什么?方法级别控制该 Bean;类级别影响整个配置类及其内部所有 Bean。
5. 如何自定义一个 Spring Boot Starter?自动配置类应该放在哪里?
编写一个配置类并添加 @AutoConfiguration(或 @Configuration),使用 @EnableConfigurationProperties 绑定属性,@Bean 提供核心服务。在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中列出配置类全限定名。将以上打包为 Jar 供其他应用引用。
- 追问:自动配置类是否需要
@Configuration注解?使用@AutoConfiguration即可,它内部组合了@Configuration等。 - 追问:自定义 Starter 如何支持条件筛选?可在配置类或
@Bean方法上添加@ConditionalOnXxx注解。 - 追问:如何确保自定义 Starter 的自动配置在官方之后加载?使用
@AutoConfigureAfter注解指定顺序。
6. 如何禁用某个特定的自动配置类?有哪些方式?
方式一:通过配置文件 spring.autoconfigure.exclude=全限定名;方式二:在 @EnableAutoConfiguration(exclude = {Xxx.class}) 中指定;方式三:通过环境变量/启动参数覆盖;方式四:直接排除该 Starter 依赖(不推荐,会丢失其他功能)。
- 追问:排除项在什么时候生效?在
getAutoConfigurationEntry中,早于条件筛选,直接移除。 - 追问:能否排除内部嵌套的配置类?可以,只需指定嵌套类的全限定名。
- 追问:排除和
@ConditionalOnMissingBean有何区别?排除是禁用整个配置类,可能影响多个 Bean;后者是细粒度控制单个 Bean。
7. AutoConfiguration.imports 和 spring.factories 在自动配置中有什么区别?
AutoConfiguration.imports 是 Spring Boot 2.7 引入的专用文件,每行一个配置类名,简洁无 key。spring.factories 是通用 SPI 文件,key 为 EnableAutoConfiguration 接口。后者用于兼容旧版及非自动配置的 SPI 扩展。官方推荐逐步迁移至 .imports 文件。
- 追问:两者如果同时存在,哪个优先级高?加载时会合并,去重后共同工作,没有绝对的替换关系,但新文件内容优先作为初始候选。
- 追问:
ImportCandidates.load是如何实现的?它直接读取指定路径的资源文件,按行解析。 - 追问:旧项目升级后是否需要立刻迁移?不需要,但应遵循新规范。
8. DeferredImportSelector 的作用是什么?为什么自动配置需要它?
DeferredImportSelector 将导入的配置类处理时机推迟到所有用户定义的常规配置类之后,从而确保用户自定义的 Bean 先被注册。这使得自动配置的 @ConditionalOnMissingBean 能正确检测到用户已定义的 Bean,避免被覆盖。
- 追问:如果去掉 Deferred,会出现什么问题?自动配置可能先注册默认 Bean,导致用户的
@Bean无法覆盖,破坏设计原则。 - 追问:多个 DeferredImportSelector 之间如何排序?通过实现
Group和getImportGroup,并可配合@Order、Ordered接口排序。 - 追问:
@AutoConfigureBefore利用了此特性吗?是的,它通过影响自动配置类内部的顺序,从而间接影响 Deferred 组的处理顺序。
9. @EnableConfigurationProperties 内部是如何工作的?
它使用 @Import(EnableConfigurationPropertiesRegistrar.class) 注册 ImportBeanDefinitionRegistrar。在解析到该注解时,Registrar 会将注解中指定的 @ConfigurationProperties 类注册为 BeanDefinition,并添加绑定基础设施,使得 Spring 能够自动将 application.properties 的值注入到这些 Bean 中。
- 追问:必须使用这个注解才能绑定属性吗?也可以通过
@ConfigurationProperties结合@Component或@Bean方式,但自动配置类通常用此注解显式声明。 - 追问:它如何支持嵌套属性?
@ConfigurationProperties支持复杂类型,内部通过ConfigurationPropertiesBindingPostProcessor绑定。 - 追问:与
@Value有何异同?前者用于类型安全的批量绑定,后者用于单个键值注入,通常搭配使用。
10. 自动配置如何实现“用户定义的 Bean 优先”?
通过 DeferredImportSelector 确保自动配置的 Bean 在用户配置之后加载,再配合 @ConditionalOnMissingBean 检查容器中是否已存在同类型 Bean。如果存在,自动配置的 Bean 定义便被跳过,从而保证用户 Bean 的优先性。
- 追问:如果用户也使用
@Import引入自己的 ImportSelector,优先级可能被打乱,如何确保?可以通过实现Ordered接口,合理编排顺序,或使用@AutoConfigureBefore。 - 追问:多个同类型 Bean,Spring 如何选择注入?通常结合
@Primary和@Qualifier,自动配置一般不标注@Primary,用户可自行添加。 - 追问:原型 Bean 覆盖单例 Bean 会怎样?覆盖逻辑仅关心 BeanDefinition,一旦用户定义,自动配置跳过,作用域不影响覆盖语义。
11. 条件注解的评估是在什么阶段执行的?为什么有的条件不生效?
条件评估主要在 ConfigurationClassParser 处理配置类时,调用 ConditionEvaluator.shouldSkip 执行。ConfigurationClassPostProcessor 是 BDRPP,它触发解析过程。不生效的常见原因有:评估阶段不匹配(ConfigurationPhase),例如 Bean 类型条件依赖尚未注册的 Bean;或者类路径条件失败未被正确感知。另外,自动配置层面的快速过滤失败也会导致条件注解被忽略。
- 追问:如何在 Filter 和 shouldSkip 两个阶段调试?借助
debug=true输出报告,报告中会详细说明每个类的匹配情况。 - 追问:
@ConditionalOnBean对 Bean 的注册顺序敏感吗?非常敏感,默认仅能检测到在此之前已注册的 Bean,所以常需配合@AutoConfigureAfter使用。 - 追问:如何自定义 Condition?实现
Condition接口,覆写matches方法,并在@Conditional中引用。
12. (系统设计题)设计一个公司内部的 Starter,它需要根据不同的环境(dev/test/prod)自动创建不同配置的缓存 Bean(如 dev 用 Caffeine,prod 用 Redis)。请描述这个 Starter 的自动配置类结构、使用的条件注解、配置文件声明以及如何支持用户覆盖。
-
自动配置类结构:设计
CacheAutoConfiguration作为入口,内部根据条件导入不同的子配置:CaffeineCacheConfiguration、RedisCacheConfiguration。 -
条件注解:
@ConditionalOnProperty(name = "cache.type", havingValue = "caffeine")激活 Caffeine。@ConditionalOnProperty(name = "cache.type", havingValue = "redis")激活 Redis。- 添加
@ConditionalOnClass确保对应依赖存在。
-
配置文件声明:在
application-{profile}.yml中定义cache.type=caffeine或redis。 -
用户覆盖:每个子配置中的
@Bean都使用@ConditionalOnMissingBean(CacheManager.class)。用户如果需要定制,可自行创建CacheManagerBean,并设置cache.type为自定义类型,此时自动配置因类型不匹配而跳过。 -
追问:如果用户不配置
cache.type,应如何提供默认? 使用matchIfMissing = true指定一个默认配置。 -
追问:如何保证用户自定义 CacheManager 优先? 依靠
@ConditionalOnMissingBean即可。 -
追问:扩展性问题,如果未来要增加 ehcache 支持,如何不影响现有代码? 添加新的子配置类并使用相同的条件注解模式,自动配置会自动发现并匹配。
自动配置关键机制速查表
| 组件/文件/注解 | 作用 | 实现细节 | 关联前文篇章 |
|---|---|---|---|
@EnableAutoConfiguration | 自动配置总入口 | 组合 @Import(AutoConfigurationImportSelector.class) 与 @AutoConfigurationPackage | 第10篇 @Import |
AutoConfigurationImportSelector | 加载、排除、过滤候选配置类 | 实现 DeferredImportSelector,模板方法 getAutoConfigurationEntry | 第10篇 ImportSelector,第15篇启动流程 |
AutoConfigurationPackages.Registrar | 记录基准扫描包 | ImportBeanDefinitionRegistrar 实现,存储包名至 BeanFactory | 第10篇 ImportBeanDefinitionRegistrar |
META-INF/spring/...AutoConfiguration.imports | 候选自动配置列表 | Boot 2.7+ 专用文件,每行一个类名 | 第11篇 SPI 演进 |
META-INF/spring.factories (EnableAutoConfiguration) | 传统候选配置加载 | SpringFactoriesLoader.loadFactoryNames 读取 | 第11篇 |
ConfigurationClassFilter | 快速过滤自动配置类 | 持有 FilteringSpringBootCondition 列表,提前评估 @ConditionalOnClass 等 | 本文第4章 |
ConditionEvaluator.shouldSkip | 标准条件评估入口 | 解析 @Conditional 注解,实例化 Condition 并逐一匹配 | 第6篇条件注解(后续详述) |
@ConditionalOnMissingBean | 保障用户覆盖 | 在自动配置 @Bean 上使用,检测 Bean 是否已存在 | 本文第6、9章 |
@EnableConfigurationProperties | 注册属性类并绑定 | @Import(EnableConfigurationPropertiesRegistrar.class) + Binder | 本文第7章 |
DeferredImportSelector | 延迟加载自动配置 | 推迟至用户配置后处理,保证覆盖优先级 | 第10篇,本文第2、6章 |
延伸阅读
- Spring Boot 官方文档:“Creating Your Own Auto-configuration” 章节
- 《Spring Boot 编程思想》(小马哥)自动配置原理深度剖析
- Spring Boot 源码
AutoConfigurationImportSelector类注释及spring-boot-autoconfigure模块结构 - 《Spring 揭秘》第 9-10 章条件注解与 @Import 机制
- 技术博客:《Spring Boot 自动配置原理解析》系列(芋道源码等平台)
- 《Spring Boot in Action》中关于 auto-configuration 原理章节