一、引言:Spring Boot 的”魔法”
用过 Spring Boot 的开发者都对它的”零配置”印象深刻——加一个 spring-boot-starter-web 依赖,写一个 @SpringBootApplication 注解,一个 Web 应用就跑起来了。不用配置 DispatcherServlet、不用配置 ViewResolver、不用配置 EmbeddedServletContainer……这些配置都去哪了?
答案就是 Spring Boot 自动配置。
但很多人把自动配置等同于”魔法”——开箱即用,但出了问题无从下手。理解 Spring Boot 的自动配置原理,是掌握 Spring Boot 整个生态的基石。本文将从 @EnableAutoConfiguration 出发,层层拆解自动配置的加载机制、条件注解体系、以及如何自定义自动配置。
flowchart LR
subgraph Spring Boot 启动
SA[SpringApplication.run] --> |创建| AC[ApplicationContext]
AC --> |执行| RF[refresh 流程]
RF --> |触发| EAI[EnableAutoConfiguration\nAutoConfigurationImportSelector]
EAI --> |加载| METAINF[spring.factories\n自动配置类列表]
METAINF --> |条件判断| CL[Conditional 注解\n筛选生效的配置]
CL --> |注册| BEANS[Bean 注册到容器]
end
二、@SpringBootApplication——三合一的组合注解
2.1 注解解析
// @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 |
@EnableAutoConfiguration |
开启自动配置 | 核心工作机制(本文重点) |
@ComponentScan |
组件扫描 | 默认扫描当前包及其子包 |
2.2 @EnableAutoConfiguration——自动配置的入口
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 记录扫描包的基础包
@Import(AutoConfigurationImportSelector.class) // 关键:导入选择器
public @interface EnableAutoConfiguration {
// 排除指定的自动配置类
Class>[] exclude() default {};
// 排除指定的自动配置类名
String[] excludeName() default {};
}
核心就在 @Import(AutoConfigurationImportSelector.class) 这一行。 AutoConfigurationImportSelector 负责从 spring.factories 中读取所有自动配置类的名称,然后根据条件注解逐个判断是否应该生效。
三、AutoConfigurationImportSelector——自动配置加载器
3.1 核心流程
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ... {
@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);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 第三步:去重
configurations = removeDuplicates(configurations);
// 第四步:根据 @EnableAutoConfiguration 的 exclude 属性排除
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 第五步:应用过滤器(条件判断的核心)
configurations = filter(configurations, autoConfigurationMetadata);
// 第六步:触发 AutoConfigurationImportEvent 事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
flowchart TD
A[getAutoConfigurationEntry] --> B[getCandidateConfigurations\n从 spring.factories 加载]
B --> C[removeDuplicates\n去重]
C --> D[处理 exclude 排除]
D --> E[filter\n条件过滤]
E --> F[fireEvent\n发布事件]
F --> G[返回最终配置列表]
subgraph filter 内部
E1[遍历所有候选配置]
E2[读取每个配置的\n@Conditional 注解]
E3[用 ConditionEvaluator\n逐个评估]
E4[跳过不满足条件的配置]
end
E --> E1
E1 --> E2 --> E3 --> E4
3.2 加载候选配置的入口
// getCandidateConfigurations 是加载所有候选配置的关键
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
// 关键代码:从 spring.factories 文件中加载
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. "
+ "If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
// 加载的 Key 值
protected Class> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
// 即:从所有 spring.factories 中读取
// org.springframework.boot.autoconfigure.EnableAutoConfiguration= 的值
那些常见的 Starter 都包含了什么?
典型的 spring-boot-starter-web 的 JAR 中包含的 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件内容:
# 新版 Spring Boot 使用 AutoConfiguration.imports 替代 spring.factories
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
...
每个自动配置类上都标注了条件注解(@ConditionalOnXxx),这意味着:依赖存在时才加载,用户已配置时跳过。
四、条件注解体系(@Conditional)
条件注解是自动配置的”开关”,决定了哪些自动配置最终会生效。Spring Boot 提供了丰富的条件注解:
4.1 内置条件注解总览
| 注解 | 判断依据 | 典型用途 |
|---|---|---|
@ConditionalOnClass |
类路径中存在指定类 | 判断依赖是否存在 |
@ConditionalOnMissingClass |
类路径中不存在指定类 | 排除某些配置 |
@ConditionalOnBean |
容器中已存在指定 Bean | 用户已配置则跳过 |
@ConditionalOnMissingBean |
容器中不存在指定 Bean | 默认实现 |
@ConditionalOnProperty |
配置文件中存在指定属性 | 通过配置开关功能 |
@ConditionalOnResource |
类路径中存在资源文件 | 如 logback.xml |
@ConditionalOnWebApplication |
当前是 Web 应用 | Web 相关配置 |
@ConditionalOnNotWebApplication |
当前不是 Web 应用 | 非 Web 配置 |
@ConditionalOnExpression |
Spring EL 表达式 | 复杂条件判断 |
@ConditionalOnJava |
Java 版本 | 特定版本特性 |
@ConditionalOnJndi |
JNDI 资源存在 | 传统 JEE 部署 |
@ConditionalOnSingleCandidate |
只有一个候选 Bean | 优先使用唯一实现 |
4.2 条件注解的实现原理
// 以 @ConditionalOnClass 为例
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class) // 委托给 OnClassCondition
public @interface ConditionalOnClass {
// 需要存在的类(完整全限定名)
Class>[] value() default {};
// 需要存在的类名
String[] name() default {};
}
OnClassCondition 的条件判断逻辑:
class OnClassCondition extends FilteringSpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
// 1. 读取 @ConditionalOnClass 声明的类
// 2. 尝试用 Class.forName 加载
// 3. 如果所有类都能加载 → 返回 Match(true)
// 4. 如果有类加载失败 → 返回 Match(false, "missing class: xxx")
// 涉及多版本兼容的特殊处理:
// OnClassCondition 使用了"类名称字符串匹配"的方式
// 而不是真正的 Class.forName(避免加载类时触发静态初始化)
}
}
4.3 自动配置类源码示例
以 HttpEncodingAutoConfiguration 为例,看条件注解如何配合:
@Configuration(proxyBeanMethods = false) // Spring Boot 2.2+ 默认
// 条件 1:必须存在 CharacterEncodingFilter 类
@ConditionalOnClass(CharacterEncodingFilter.class)
// 条件 2:必须是 Web 应用环境
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 条件 3:配置文件中 spring.http.encoding.enabled ≠ false(默认 true)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled",
matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
// 条件:用户未手动定义 CharacterEncodingFilter Bean 时才生效
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
}
注意条件注解的组合使用: 一个自动配置类可能包含多个 @Conditional 条件,所有条件都必须满足,这个自动配置类才会生效。
五、自动配置的条件评估链
5.1 ConditionEvaluator
条件注解的最终评估由 ConditionEvaluator 执行:
// Spring 容器在注册 Bean 前的评估
class ConditionEvaluator {
public boolean shouldSkip(AnnotatedTypeMetadata metadata,
ConfigurationPhase phase) {
// 读取所有 @Conditional 注解
for (Condition condition : getConditions(metadata)) {
ConfigurationPhase conditionPhase = getConditionPhase(condition);
// 如果是 PARSE_CONFIGURATION 阶段的条件(如 @ConditionalOnClass)
// 在配置类解析阶段就会评估
// 如果是 REGISTER_BEAN 阶段的条件(如 @ConditionalOnMissingBean)
// 在 Bean 注册阶段评估
ConfigurationPhase actualPhase = (conditionPhase != null)
? conditionPhase : phase;
if (!condition.matches(this.registry, actualPhase.getMetadata())) {
// 条件不满足,跳过
return true;
}
}
return false;
}
}
5.2 条件过滤的时机分层
flowchart TD
subgraph 第一阶段:配置类解析阶段
P1[@ConditionalOnClass]
P2[@ConditionalOnMissingClass]
P3[@ConditionalOnResource]
P4[@ConditionalOnWebApplication]
P5[@ConditionalOnNotWebApplication]
P6[@ConditionalOnJava]
P1 -->|全部通过| NEXT1[进入 Bean 注册阶段]
end
subgraph 第二阶段:Bean 注册阶段
R1[@ConditionalOnBean]
R2[@ConditionalOnMissingBean]
R3[@ConditionalOnProperty]
R4[@ConditionalOnExpression]
R5[@ConditionalOnSingleCandidate]
R1 -->|全部通过| NEXT2[Bean 真正注册到容器]
end
这种两阶段过滤确保了:
– 类路径上缺少依赖的配置类尽早被排除(避免 ClassNotFoundException)
– Bean 是否重复的检查在容器具备完整上下文后进行
六、自动配置的顺序控制
6.1 @AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter
自动配置类之间的依赖关系通过这三个注解控制:
// AOP 自动配置必须在事务自动配置之后
@Configuration
@AutoConfigureAfter(TransactionAutoConfiguration.class)
@ConditionalOnClass(TransactionInterceptor.class)
public class AopAutoConfiguration {
// ...
}
// Jackson 自动配置需要在 Gson 自动配置之前
@Configuration
@AutoConfigureBefore(GsonAutoConfiguration.class)
public class JacksonAutoConfiguration {
// ...
}
6.2 AutoConfigurationSorter——排序算法
Spring Boot 使用拓扑排序(Topological Sorting)来确定自动配置类的加载顺序:
class AutoConfigurationSorter {
private final ConfigurableListableBeanFactory beanFactory;
private final AutoConfigurationMetadata autoConfigurationMetadata;
List<String> getInPriorityOrder(Collection<String> classNames) {
// 1. 构建类名到排序信息的映射
// 2. 处理 @AutoConfigureBefore/@AutoConfigureAfter
// 3. 处理 @AutoConfigureOrder
// 4. 检查循环依赖(如果有则报错)
// 5. 返回排序后的列表
}
// 核心的拓扑排序实现
private void sort(Set<String> classes,
MultiValueMap<String, String> byBefore,
MultiValueMap<String, String> byAfter) {
// 构建依赖图 → 拓扑排序
// 如果发现循环引用,抛出 AutoConfigurationCycleException
}
}
七、自定义自动配置
理解了原理之后,我们可以动手实现一个自定义的自动配置。
7.1 创建一个完整自定义自动配置
// 第一步:定义服务接口
public interface GreetingService {
String greet(String name);
}
// 第二步:实现类
public class DefaultGreetingService implements GreetingService {
private String prefix;
private String suffix;
public DefaultGreetingService(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
@Override
public String greet(String name) {
return prefix + " " + name + " " + suffix;
}
}
// 第三步:定义属性类(自动绑定 application.properties 中的配置)
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
// 默认值
private String prefix = "Hello"; // greeting.prefix
private String suffix = "!"; // greeting.suffix
// getters & setters
public String getPrefix() { return prefix; }
public void setPrefix(String prefix) { this.prefix = prefix; }
public String getSuffix() { return suffix; }
public void setSuffix(String suffix) { this.suffix = suffix; }
}
// 第四步:自动配置类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean(GreetingService.class)
public GreetingService greetingService(GreetingProperties properties) {
return new DefaultGreetingService(
properties.getPrefix(),
properties.getSuffix()
);
}
}
7.2 注册自动配置
// 方式 1:Spring Boot 2.7+ 推荐方式
// 在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中添加:
// com.example.autoconfigure.GreetingAutoConfiguration
// 方式 2:传统的 spring.factories(Spring Boot 2.7 之前)
// 在 META-INF/spring.factories 中添加:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.autoconfigure.GreetingAutoConfiguration
// 方式 3:生成 spring-autoconfigure-metadata.properties
// 帮助 Spring Boot 跳过条件不满足的配置,加快启动速度
// META-INF/spring-autoconfigure-metadata.properties
// com.example.autoconfigure.GreetingAutoConfiguration=\
// ConditionalOnClass=com.example.autoconfigure.GreetingService
7.3 启动速度优化——自动配置元数据
当项目中有大量自动配置类时,Spring Boot 在启动时仍然需要加载每个配置类来计算条件注解。自动配置元数据文件可以帮助 Spring Boot 预先跳过不可能生效的配置类:
# spring-autoconfigure-metadata.properties
# 格式:全限定类名.条件类型=条件值
#
# 这个文件让 Spring Boot 在类加载之前就能判断:
# 如果 GreetingService 类不存在,则 GreetingAutoConfiguration 一定不生效
# 无需加载这个类!
com.example.autoconfigure.GreetingAutoConfiguration=\
ConditionalOnClass=com.example.autoconfigure.GreetingService
八、常见问题排查
8.1 自动配置不生效
# 启用 debug 日志,查看哪些自动配置生效/不生效
# application.properties 中添加:
debug=true
# 启动时输出:
# Positive matches: (生效的自动配置)
# -----------------
# HttpEncodingAutoConfiguration matched:
# - @ConditionalOnClass found required class
# 'org.springframework.web.filter.CharacterEncodingFilter'
# - @ConditionalOnProperty (spring.http.encoding.enabled) matched
#
# Negative matches: (未生效的自动配置)
# -----------------
# RedisAutoConfiguration:
# Did not match:
# - @ConditionalOnClass did not find required class
# 'org.springframework.data.redis.core.RedisOperations'
#
# Exclusions:
# -----------
# None
8.2 常用的调试方法
// 方法 1:编程方式查看已注册的自动配置
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(DemoApplication.class, args);
// 查看所有自动配置相关的 Bean
String[] beanNames = context.getBeanNamesForType(ApplicationContext.class);
// 输出所有注册的 Bean
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
// 方法 2:使用 Actuator 端点查看
// 添加依赖后访问 /actuator/conditions
//
// org.springframework.boot
// spring-boot-starter-actuator
//
// application.properties:
// management.endpoints.web.exposure.include=conditions
九、Spring Boot 2.7+ vs 3.x 的变化
| 变化 | Spring Boot 2.7 | Spring Boot 3.x |
|---|---|---|
| 配置加载文件 | spring.factories(兼容)+ AutoConfiguration.imports |
仅 AutoConfiguration.imports |
| Java 基线 | JDK 8+ | JDK 17+ |
| Spring 基线 | Spring 5 | Spring 6 |
| Jakarta EE | javax.* | jakarta.* |
| 自动配置排序 | 支持 | 继续支持 |
// Spring Boot 3.x 完全移除对 spring.factories 的支持
// 统一使用新的注册方式:
// META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
十、总结
-
@SpringBootApplication 是一个三合一的组合注解。 @EnableAutoConfiguration、@SpringBootConfiguration、@ComponentScan 各司其职,共同实现 Spring Boot 的”零配置”体验。
-
AutoConfigurationImportSelector 是自动配置的加载引擎。 它通过 SpringFactoriesLoader 从 classpath 中加载所有候选配置类,再经过条件注解逐层过滤,最终将有效的配置类注册到容器。
-
条件注解是自动配置的”开关”。 @ConditionalOnClass 判断依赖是否存在,@ConditionalOnMissingBean 判断用户是否已自定义。只有所有条件都满足时,自动配置才会生效。这种设计保证了自动配置的”智能”——当用户提供了自定义实现时,自动配置自动退让。
-
自动配置可以排序和控制。 @AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter 控制配置类的加载顺序,避免依赖问题。
-
自定义自动配置并不复杂。 定义 ConfigurationProperties + AutoConfiguration + 注册到 AutoConfiguration.imports,三步即可创建一个规范的自动配置模块。
-
debug=true 是最好的调试工具。 当自动配置不按预期生效时,启动日志中的 Positive matches 和 Negative matches 是最直接的排查线索。


暂无评论内容