一篇文章让你彻底弄懂Spring Boot 自动配置原理详解

Spring Boot 自动配置原理详解

适用于:Spring Boot 2.x / 3.x,偏向原理 + 面试


1. 为什么需要自动配置?

1.1 传统 Spring 配置的痛点

传统 Spring 应用一般要:

  • 写 XML 或大量 @Configuration
  • 手动配置数据源、事务管理器、视图解析器、消息转换器……
  • 新增一个技术栈(如 Redis、MQ)都要查一堆文档,抄一堆模板代码

问题:

  • 重复劳动多,配置啰嗦
  • 很多项目都是“复制上一份配置,改一点点”
  • 配置错误很难排查

1.2 Spring Boot 的目标

Spring Boot 的核心理念可以简单概括为:

  • 约定大于配置
  • 能推断出来的,就不要你写

自动配置(Auto-Configuration)就是实现这个目标的关键机制:
根据 classpath、配置文件和当前环境,自动帮你注册合适的 Bean。


2. 自动配置涉及的核心注解与组件

2.1 @SpringBootApplication 做了什么?

典型启动类:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@SpringBootApplication 是一个复合注解,本质等价于:

@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {}

其中与自动配置直接相关的是:@EnableAutoConfiguration


2.2 @EnableAutoConfiguration 的作用

精简理解:

  • 告诉 Spring Boot:请根据当前依赖和环境,帮我把常用组件都自动配置好
  • 底层是通过 @Import(AutoConfigurationImportSelector.class) 把大量自动配置类导入到容器中

源码(简化):

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    // 允许排除某些自动配置类
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

重点:AutoConfigurationImportSelector 是自动配置机制的“中枢”。


2.3 AutoConfigurationImportSelector 的核心流程

这个类在启动时会做几件事(简化版逻辑):

  1. 加载候选自动配置类列表

    • 从类路径下的配置文件读取所有“自动配置类”的全限定类名
    • Spring Boot 3 之前:META-INF/spring.factories
    • Spring Boot 3:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  2. 去重 + 合并

  3. 处理排除项

    • 注解上的 exclude / excludeName
    • 配置文件中的 spring.autoconfigure.exclude
  4. 应用各种 @Conditional* 条件,筛选出真正要生效的自动配置类

  5. 把这些类当作普通 @Configuration 导入容器

伪代码示意(极度简化):

List<String> candidates = getCandidateConfigurations();   // 所有候选
candidates = removeDuplicates(candidates);                // 去重
Set<String> exclusions = getExclusions();                 // 需要排除的类
candidates.removeAll(exclusions);                         // 删除排除项
candidates = filter(candidates, autoConfigurationMetadata); // 按条件过滤
return candidates;                                        // 导入容器

2.4 @Configuration vs @AutoConfiguration(Spring Boot 3)

在 Spring Boot 2.x 中,自动配置类一般写成:

@Configuration
public class WebMvcAutoConfiguration {
    // ...
}

在 Spring Boot 3 中,官方推荐使用新的注解:

@AutoConfiguration
public class WebMvcAutoConfiguration {
    // ...
}

@AutoConfiguration 本质上也是一种 @Configuration,只是专门标识“这是自动配置类”,并有一些默认优化(比如 proxyBeanMethods = false)。


3. 自动配置的加载流程(启动过程)

从应用启动的角度,把整个流程串起来:

  1. 调用 SpringApplication.run(...)
  2. SpringApplication 创建并刷新 ApplicationContext
  3. 解析主类上的 @SpringBootApplication
  4. 处理其中的 @EnableAutoConfiguration
    • 导入 AutoConfigurationImportSelector
  5. AutoConfigurationImportSelector
    • 读取所有候选自动配置类名(spring.factories / AutoConfiguration.imports
    • 去重、排除、按条件过滤
    • 返回最终要导入的自动配置类列表
  6. Spring 容器当作普通配置类处理这些自动配置类:
    • 解析 @Bean
    • 解析 @Import
    • 解析 @ConfigurationProperties 等等
  7. 所有 Bean 定义注册完毕,容器完成初始化

可以把它想象成一条流水线:

@SpringBootApplication
        │
        ▼
@EnableAutoConfiguration
        │ import
        ▼
AutoConfigurationImportSelector
        │
        ├─ 读取候选类(配置文件)
        ├─ 去重 & 排除
        ├─ 条件评估(@Conditional 系列)
        ▼
注册自动配置类 -> 注册 Bean

4. 条件装配:@Conditional 家族

自动配置的精髓:有条件地注册 Bean
这些条件就是一系列 @Conditional* 注解。

4.1 常见的条件注解

  • @ConditionalOnClass
    classpath 中存在指定类时,配置才生效

  • @ConditionalOnMissingClass
    classpath 中不存在某个类时才生效

  • @ConditionalOnBean
    容器中已经存在指定 Bean 时生效

  • @ConditionalOnMissingBean
    容器中没有指定 Bean 时生效(非常常见,用于“默认实现,可被用户覆盖”)

  • @ConditionalOnProperty
    某个配置属性存在 / 为特定值时生效

  • @ConditionalOnResource
    classpath 中存在某个资源文件时生效

  • @ConditionalOnWebApplication / @ConditionalOnNotWebApplication
    当前是否 Web 应用(Servlet / Reactive)

  • @ConditionalOnExpression
    SpEL 表达式结果为 true 时生效

  • @ConditionalOnJava
    JDK 版本条件判断

4.2 典型例子:按 classpath 判断

@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    // ...
}

含义:

  • 只有在 classpath 上有 DataSource 相关依赖时,才去自动配置数据源
  • 换句话说:你引了哪种 starter,就会触发对应的自动配置

4.3 典型例子:默认 Bean,可被覆盖

@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}

含义:

  • 如果用户自己没有声明 ObjectMapper Bean,则用默认的这一份
  • 如果用户自定义了 Bean,自动配置这段就会“让路”

5. Spring Boot 2 vs 3:自动配置注册方式的演进

5.1 Spring Boot 2.x:spring.factories

老版本中,自动配置类通过 spring.factories 注册:

# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.autoconfig.ExampleAutoConfiguration,com.example.autoconfig.OtherAutoConfiguration

特点:

  • key 是 org.springframework.boot.autoconfigure.EnableAutoConfiguration
  • value 是一串自动配置类全限定名,用逗号分隔

5.2 Spring Boot 3:AutoConfiguration.imports

从 Spring Boot 2.7 开始,引入新的配置文件;到了 Spring Boot 3,已经完全改用:

# src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.autoconfig.ExampleAutoConfiguration
com.example.autoconfig.OtherAutoConfiguration

特点:

  • 每行一个自动配置类
  • 文件路径固定:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • 更加专用、清晰,避免 spring.factories 一坨东西混一起

如果你自己写 starter,要注意:

  • Boot 2.x:可以用 spring.factories(2.7 也支持新方式)
  • Boot 3.x:必须使用 AutoConfiguration.importsspring.factories 已经不再支持自动配置注册

6. 从零实现一个自定义自动配置 + Starter

下面用一个非常简单的例子,走一遍完整流程。

6.1 模块划分建议

一般推荐拆成三层(也可以简化):

  1. 业务模块(可选)
    • 比如 demo-library,只放业务类:HelloServiceHelloProperties
  2. 自动配置模块
    • 比如 demo-spring-boot-autoconfigure
    • 放自动配置类、AutoConfiguration.imports
  3. Starter 模块
    • 比如 demo-spring-boot-starter
    • 对外暴露,只需引入这一个依赖

6.2 配置属性类

// prefix = demo.hello
@ConfigurationProperties(prefix = "demo.hello")
public class HelloProperties {
    /**
     * 前缀,默认 Hello
     */
    private String prefix = "Hello";

    /**
     * 后缀,默认 !
     */
    private String suffix = "!";

    // getter/setter ...
}

6.3 业务 Service

public class HelloService {

    private final HelloProperties properties;

    public HelloService(HelloProperties properties) {
        this.properties = properties;
    }

    public String sayHello(String name) {
        return properties.getPrefix() + " " + name + properties.getSuffix();
    }
}

6.4 自动配置类

Spring Boot 3 写法(推荐):

@AutoConfiguration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public HelloService helloService(HelloProperties properties) {
        return new HelloService(properties);
    }
}

Spring Boot 2 写法可以用 @Configuration 替代 @AutoConfiguration

说明:

  • @AutoConfiguration:标记这是一个自动配置类
  • @EnableConfigurationProperties:让 HelloProperties 生效,绑定配置属性
  • @ConditionalOnMissingBean:允许用户自定义自己的 HelloService 覆盖默认实现

6.5 在 AutoConfiguration.imports 中注册

src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

com.example.demo.autoconfigure.HelloAutoConfiguration

只要这个 jar 在 classpath 上,Spring Boot 就能自动发现并执行里面的自动配置。

6.6 Starter POM 示例

demo-spring-boot-starter/pom.xml 中典型依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>com.example</groupId>
        <artifactId>demo-spring-boot-autoconfigure</artifactId>
        <version>${project.version}</version>
    </dependency>
</dependencies>

用户项目只需要:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>demo-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

再在 application.yml 中写:

demo:
  hello:
    prefix: "Hi"
    suffix: "~~"

然后就可以直接注入使用:

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @GetMapping("/hello")
    public String hello(String name) {
        return helloService.sayHello(name);
    }
}

不需要在业务项目里写任何 @Configuration 相关代码。


7. 自动配置是怎么“感知环境”的?

总结一下自动配置如何做决策——它主要看三类信息:

  1. Classpath 上的依赖

    • spring-boot-starter-web
      • 生效 DispatcherServletAutoConfigurationWebMvcAutoConfiguration
    • spring-boot-starter-data-jpa
      • 生效 DataSourceAutoConfigurationHibernateJpaAutoConfiguration
    • 有 Redis 相关依赖:
      • 生效 RedisAutoConfiguration
  2. 容器中已有的 Bean

    • 借助 @ConditionalOnBean / @ConditionalOnMissingBean 判断
    • 典型模式:默认实现 + 用户可覆盖
  3. 配置文件中的属性

    • @ConditionalOnProperty 根据 application.yml / application.properties 中的值来控制
    • 比如很多 xxx.enabled 属性,都是通过这个注解控制开关

你可以理解为:自动配置就是把“如果……就帮你配好……”这种规则写在了一大堆配置类里。


8. 自动配置的调试与问题排查

实际开发中,经常会遇到这些问题:

  • “为什么某个自动配置生效了?”
  • “为什么某个自动配置没有生效?”
  • “我写的 Bean 为什么被覆盖 / 没有生效?”

这时候要学会看“条件评估报告”。

8.1 启动时打开 debug 日志

在配置文件中加入:

debug=true

启动后日志里会出现一段类似:

=========================
AUTO-CONFIGURATION REPORT
=========================

Positive matches:
   ...

Negative matches:
   ...

可以看到:

  • 哪些自动配置类被激活(Positive matches)
  • 哪些被禁用,以及禁用原因(Negative matches)

8.2 使用 Actuator /actuator/conditions 端点

引入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置开启端点:

management:
  endpoints:
    web:
      exposure:
        include: conditions

启动后访问:/actuator/conditions
可以看到结构化的 JSON 报告,比日志更方便分析。

8.3 常见排查套路

  1. 打开 debug=true/actuator/conditions 看具体是哪个条件没满足
  2. 检查 classpath 上是否有相关依赖(starter 引入是否正确)
  3. 检查有没有在注解 / 配置中 exclude 了某些自动配置类
  4. 检查有没有手动声明了同名 Bean,导致自动配置失效或被覆盖
  5. 检查是否启用了错误的 profile / 配置文件

9. 常见面试题 & 回答要点

9.1 问:Spring Boot 自动配置的原理是什么?

答题要点:

  1. 启动类上的 @SpringBootApplication 包含 @EnableAutoConfiguration
  2. @EnableAutoConfiguration 通过 AutoConfigurationImportSelector 加载所有候选自动配置类
  3. 候选类来源于:
    • Spring Boot 2.x:META-INF/spring.factories
    • Spring Boot 3.x:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  4. 利用 @Conditional* 注解,根据 classpath、已有 Bean、配置属性等条件筛选
  5. 最终把满足条件的自动配置类作为普通 @Configuration 导入容器,注册 Bean

一句话总结:“EnableAutoConfiguration + AutoConfigurationImportSelector + 一堆带条件的配置类”。


9.2 问:Spring Boot 2 和 3 在自动配置上的主要区别?

答题要点:

  • 注册自动配置类的方式发生变化:
    • 2.x 使用 spring.factories
    • 3.x 使用 AutoConfiguration.imports,并废弃前者
  • 自动配置类推荐使用 @AutoConfiguration 注解
  • 底层核心思想不变:依然是 AutoConfigurationImportSelector 负责加载 + 条件过滤

9.3 问:如何自定义一个 Spring Boot Starter?

答题思路可按步骤说:

  1. 定义业务 Bean 和配置属性类(@ConfigurationProperties
  2. 编写自动配置类:
    • @AutoConfiguration / @Configuration
    • 配合 @ConditionalOnClass / @ConditionalOnMissingBean
    • 使用 @EnableConfigurationProperties 激活属性绑定
  3. META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中注册自动配置类
  4. 创建 starter 模块:
    • 依赖 spring-boot-starter 和自动配置模块
    • 对外发布这个 starter 依赖
  5. 使用时只需引入 starter + 少量配置即可

9.4 问:如何关闭某个自动配置?

三种常用方式:

  1. 在注解上排除:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication {}
  1. 在配置文件中排除:
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  1. 使用特定的开关属性(如果该自动配置提供了):
spring.datasource.enabled=false

10. 总结

  • 自动配置本质上就是:一堆带条件的 @Configuration
  • 入口:@SpringBootApplication@EnableAutoConfigurationAutoConfigurationImportSelector
  • 数据来源:classpath、已有 Bean、配置属性、运行环境
  • 注册方式演进:
    • 2.x:spring.factories
    • 3.x:AutoConfiguration.imports
  • 使用层面要掌握:
    • 怎么关(exclude / 属性开关)
    • 怎么查(debug 日志、/actuator/conditions
    • 怎么写(自定义自动配置 + starter)

如果你能把这里的流程用自己的话完整讲一遍,再结合一两个真实项目中的例子(比如“我们自定义了一个xx starter”),在面试里基本就能拿到这块的高分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值