Spring Boot 自动配置源码级解析

一、引言: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

十、总结

  1. @SpringBootApplication 是一个三合一的组合注解。 @EnableAutoConfiguration、@SpringBootConfiguration、@ComponentScan 各司其职,共同实现 Spring Boot 的”零配置”体验。

  2. AutoConfigurationImportSelector 是自动配置的加载引擎。 它通过 SpringFactoriesLoader 从 classpath 中加载所有候选配置类,再经过条件注解逐层过滤,最终将有效的配置类注册到容器。

  3. 条件注解是自动配置的”开关”。 @ConditionalOnClass 判断依赖是否存在,@ConditionalOnMissingBean 判断用户是否已自定义。只有所有条件都满足时,自动配置才会生效。这种设计保证了自动配置的”智能”——当用户提供了自定义实现时,自动配置自动退让。

  4. 自动配置可以排序和控制。 @AutoConfigureOrder、@AutoConfigureBefore、@AutoConfigureAfter 控制配置类的加载顺序,避免依赖问题。

  5. 自定义自动配置并不复杂。 定义 ConfigurationProperties + AutoConfiguration + 注册到 AutoConfiguration.imports,三步即可创建一个规范的自动配置模块。

  6. debug=true 是最好的调试工具。 当自动配置不按预期生效时,启动日志中的 Positive matches 和 Negative matches 是最直接的排查线索。

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容