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

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

1.1 传统 Spring 配置的痛点

传统 Spring 应用一般要:

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

问题:

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

1.2 Spring Boot 的目标

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

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

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


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

2.1 @SpringBootApplication 做了什么?

典型启动类:

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

AI写代码java
运行
123456

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

less 复制代码
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {}

AI写代码java
运行
1234

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


2.2 @EnableAutoConfiguration 的作用

精简理解:

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

源码(简化):

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

AI写代码java
运行
1234567

重点: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 导入容器

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

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

AI写代码java
运行
123456

2.4 @Configuration vs @AutoConfiguration(Spring Boot 3)

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

kotlin 复制代码
@Configuration
public class WebMvcAutoConfiguration {
    // ...
}

AI写代码java
运行
1234

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

kotlin 复制代码
@AutoConfiguration
public class WebMvcAutoConfiguration {
    // ...
}

AI写代码java
运行
1234

@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 定义注册完毕,容器完成初始化

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

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

AI写代码text
12345678910111213

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 判断

less 复制代码
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    // ...
}

AI写代码java
运行
12345

含义:

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

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

less 复制代码
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
    return new ObjectMapper();
}

AI写代码java
运行
12345

含义:

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

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

5.1 Spring Boot 2.x:spring.factories

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

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

AI写代码properties
12

特点:

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

5.2 Spring Boot 3:AutoConfiguration.imports

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

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

AI写代码text
123

特点:

  • 每行一个自动配置类
  • 文件路径固定: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 配置属性类

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

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

    // getter/setter ...
}

AI写代码java
运行
123456789101112131415

6.3 业务 Service

arduino 复制代码
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();
    }
}

AI写代码java
运行
123456789101112

6.4 自动配置类

Spring Boot 3 写法(推荐):

less 复制代码
@AutoConfiguration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloAutoConfiguration {

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

AI写代码java
运行
12345678910

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

arduino 复制代码
com.example.demo.autoconfigure.HelloAutoConfiguration

AI写代码text
1

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

6.6 Starter POM 示例

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

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>

AI写代码xml
123456789101112

用户项目只需要:

xml 复制代码
<dependency>
    <groupId>com.example</groupId>
    <artifactId>demo-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>

AI写代码xml
12345

再在 application.yml 中写:

yaml 复制代码
demo:
  hello:
    prefix: "Hi"
    suffix: "~~"

AI写代码yaml
1234

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

typescript 复制代码
@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

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

AI写代码java
运行
1234567891011

不需要在业务项目里写任何 @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 日志

在配置文件中加入:

ini 复制代码
debug=true

AI写代码properties
1

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

markdown 复制代码
=========================
AUTO-CONFIGURATION REPORT
=========================

Positive matches:
   ...

Negative matches:
   ...

AI写代码text
123456789

可以看到:

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

8.2 使用 Actuator /actuator/conditions 端点

引入:

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

AI写代码xml
1234

配置开启端点:

yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: conditions

AI写代码yaml
12345

启动后访问:/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. 在注解上排除:
kotlin 复制代码
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication {}

AI写代码java
运行
12
  1. 在配置文件中排除:
ini 复制代码
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

AI写代码properties
1
  1. 使用特定的开关属性(如果该自动配置提供了):
ini 复制代码
spring.datasource.enabled=false

AI写代码properties
1

10. 总结

  • 自动配置本质上就是:一堆带条件的 @Configuration

  • 入口:@SpringBootApplication@EnableAutoConfigurationAutoConfigurationImportSelector

  • 数据来源:classpath、已有 Bean、配置属性、运行环境

  • 注册方式演进:

    • 2.x:spring.factories
    • 3.x:AutoConfiguration.imports
  • 使用层面要掌握:

    • 怎么关(exclude / 属性开关)
    • 怎么查(debug 日志、/actuator/conditions
    • 怎么写(自定义自动配置 + starter)

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

相关推荐
Java水解2 小时前
【MYSQL】MYSQL学习的一大重点:MYSQL数据类型
后端·mysql
架构师沉默2 小时前
为什么 Dubbo 从 ZooKeeper 转向 Nacos?
java·后端·架构
Zzxy2 小时前
Redis集成与基础操作
spring boot·redis
用户8307196840822 小时前
Spring Prototype Bean的四种正确使用方式
java·spring boot·后端
代码丰2 小时前
一篇讲透:Spring Boot + Redisson + 注解 + AOP 实现接口限流(可直接落地)
后端
蚂蚁集团分布式架构3 小时前
🦐 不办 Meetup,开挑战赛!SOFAStack PR Challenge | SOFAStack 8 周年
后端·github·claude
untE EADO3 小时前
SpringBoot:几种常用的接口日期格式化方法
java·spring boot·后端
青槿吖4 小时前
第一篇:Redis集群从入门到踩坑:3主3从保姆级搭建+核心原理一次性讲透|面试必看
前端·redis·后端·面试·职场和发展·bootstrap·html
与硝酸4 小时前
从 Claude Code 源码看 Agent 系统设计:主流框架都在解决的问题与各自的解法
人工智能·后端