在前面十多篇里,我们拆解了 IoC 容器、Spring MVC、AOP、事务管理------这些是 Spring 框架的核心能力。但你有没有想过一个问题:在 Spring Boot 出现之前,配置这些东西有多麻烦?
你需要写 web.xml 配置 DispatcherServlet,需要写 applicationContext.xml 配置数据源、事务管理器、视图解析器,需要写 spring-mvc.xml 配置组件扫描......一个项目光 XML 配置文件就有三四个,加起来几百行。
Spring Boot 的出现,把这一切都简化成了几行 application.yml 和一个 @SpringBootApplication 注解。
这一篇,我们把 Spring Boot 自动配置的底层逻辑拆开来看------从 @SpringBootApplication 的组成,到 @EnableAutoConfiguration 的工作原理,再到 spring.factories 和 AutoConfiguration.imports 的加载机制。
学习目标
- 理解 Spring Boot "约定优于配置" 的设计哲学
- 掌握
@SpringBootApplication组合注解的三个核心成员 - 深入理解
@EnableAutoConfiguration的工作原理 - 了解
spring.factories/AutoConfiguration.imports文件的作用 - 掌握
@Conditional条件注解家族的使用 - 了解如何编写自定义 Starter
正文
一、"约定优于配置"是什么?
"约定优于配置" (Convention over Configuration)是 Spring Boot 最核心的设计理念。它的意思是:框架基于合理的默认约定来工作,开发者只需要在偏离约定时才进行显式配置。
这句话听起来有点抽象,我们用一个具体的例子来说明。
在 Spring MVC 中,视图解析器有一个常见的配置:你需要告诉 Spring,JSP 文件放在哪里、后缀是什么。
xml
<!-- 传统的 Spring MVC 配置 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
</bean>
这段配置告诉 Spring:视图文件在 /WEB-INF/views/ 目录下,后缀是 .jsp 。
Spring Boot 的约定是:如果你用 Thymeleaf,模板文件放在 src/main/resources/templates/ 下,后缀是 .html 。如果你遵循这个约定,就不需要任何配置。
这就是"约定优于配置"------框架有一个默认的约定,你遵循它就能零配置工作。只有当你需要偏离约定时(比如想把模板放在其他目录),才需要显式配置。
Spring Boot 中的典型约定:
| 约定项 | Spring Boot 默认值 |
|---|---|
| 配置文件位置 | src/main/resources/application.yml |
| 静态资源位置 | src/main/resources/static/ 或 public/ |
| 模板文件位置 | src/main/resources/templates/ |
| 组件扫描路径 | 启动类所在包及其子包 |
| 嵌入式容器端口 | 8080 |
| 数据库连接池 | HikariCP |
这些约定不是"写死的",而是可覆盖的 ------你可以在 application.yml 中修改它们。但如果你不修改,框架就用默认值帮你工作。
从"XML 地狱"到"零配置" :
在 Spring Boot 之前,一个典型的 Spring 项目需要这些配置文件:
web.xml:配置 DispatcherServlet、Filter、ListenerapplicationContext.xml:配置数据源、事务管理器、Service 扫描spring-mvc.xml:配置视图解析器、HandlerMapping、组件扫描mybatis-config.xml:配置 MyBatis 设置
加起来少则几百行,多则上千行。Spring Boot 用自动配置取代了这些 XML------你只需要引入对应的 Starter,框架就会根据类路径中的依赖自动推断并应用合适的配置。
二、@SpringBootApplication 拆解:三个注解的组合
@SpringBootApplication 是 Spring Boot 最核心的注解。我们点开它的源码看看:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
// ...
}
@SpringBootApplication 是一个组合注解,它聚合了三个核心注解:
| 注解 | 作用 |
|---|---|
@SpringBootConfiguration |
表明这是一个 Spring Boot 配置类(等同于 @Configuration) |
@EnableAutoConfiguration |
启用自动配置------这是 Spring Boot 自动配置的总开关 |
@ComponentScan |
启用组件扫描,默认扫描当前类所在包及其子包 |
@SpringBootConfiguration :它本质上就是 @Configuration,只是 Spring Boot 单独定义了一个注解来标识"这是一个 Spring Boot 配置类"。
@ComponentScan :默认扫描启动类所在包及其子包下的所有 @Component(以及 @Service、@Controller、@Repository 等衍生注解)。这意味着你放在启动类所在包外面的组件不会被扫描到------这是新手常踩的坑之一。
@EnableAutoConfiguration :这是自动配置的核心。它告诉 Spring Boot:"根据类路径中的依赖,自动推断并应用合适的配置。"
三、@EnableAutoConfiguration 的奥秘
@EnableAutoConfiguration 是 Spring Boot 自动配置的总开关。我们来看它的源码:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@EnableAutoConfiguration 做了两件关键的事情:
第一件:@AutoConfigurationPackage
这个注解通过 @Import(AutoConfigurationPackages.Registrar.class) 导入了一个 Registrar,它会记录启动类所在的包路径 。这个包路径随后会被用于 @ComponentScan 的默认扫描范围。
第二件:@Import(AutoConfigurationImportSelector.class)
这是自动配置的核心机制 。AutoConfigurationImportSelector 实现了 ImportSelector 接口,它的作用是:在 Spring 容器启动时,动态加载所有自动配置类。
AutoConfigurationImportSelector 的工作流程是这样的:
- Spring 在解析
@Import时,调用AutoConfigurationImportSelector.selectImports()方法 - 该方法读取配置文件,获取所有自动配置类的全限定名列表
- 对这些配置类进行去重 和排除 处理(根据
exclude和excludeName属性) - 返回最终需要加载的配置类名称数组
- Spring 将这些配置类作为 Bean 定义加载到容器中
关键认知 :@EnableAutoConfiguration 本身不包含任何配置类------它只是一个"开关"。真正干活的是 AutoConfigurationImportSelector,它负责发现并加载所有的自动配置类。
四、spring.factories 的约定:自动配置类的"清单"
那么,AutoConfigurationImportSelector 是从哪里读取自动配置类的呢?
答案是:META-INF/spring.factories 文件。
在 Spring Boot 2.x 及之前的版本中,每个 Starter 的 JAR 包中都包含一个 META-INF/spring.factories 文件。这个文件的内容格式是 key=value ,其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 这个 key 对应的 value 就是自动配置类的全限定名列表。
# META-INF/spring.factories 示例(Spring Boot 2.x)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
Spring Boot 3.x 的变化:
从 Spring Boot 3.0 开始,spring.factories 文件被废弃了 ,取而代之的是 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件。
新的文件格式更简洁:每行一个配置类的全限定名,不需要 key-value 结构。
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
为什么要改变?
spring.factories 文件被用于多种目的(自动配置、监听器、初始化器等),所有配置都挤在一个文件里,容易混乱。新的 .imports 文件职责更单一------只用于自动配置类的声明,更加清晰和模块化。
兼容性说明 :如果你在 Spring Boot 3.x 项目中仍然使用 spring.factories 来声明自动配置类,它们不会被加载 。必须迁移到新的 .imports 文件格式。
五、@Conditional 条件配置:为什么自动配置不会"过度配置"
你可能会问:Spring Boot 加载了这么多自动配置类,难道它们全都生效吗?
答案是否定的。如果所有自动配置类都生效,你的应用会启动得非常慢,而且会加载大量不需要的 Bean。
Spring Boot 通过 @Conditional 系列注解 来控制自动配置类是否生效 。每个自动配置类上都标注了一个或多个 @Conditional 注解,只有条件满足时,这个配置类才会被加载。
最常用的条件注解:
| 注解 | 条件 |
|---|---|
@ConditionalOnClass |
classpath 中存在指定的类时才生效 |
@ConditionalOnMissingClass |
classpath 中不存在指定的类时才生效 |
@ConditionalOnBean |
容器中存在指定的 Bean 时才生效 |
@ConditionalOnMissingBean |
容器中不存在指定的 Bean 时才生效 |
@ConditionalOnProperty |
配置文件中存在指定的属性且值匹配时才生效 |
@ConditionalOnWebApplication |
当前应用是 Web 应用时才生效 |
@ConditionalOnMissingBean 的特殊意义:
这个注解是 Spring Boot 自动配置可覆盖性 的关键。它的含义是:只有当开发者没有自定义该类型的 Bean 时,自动配置才会创建默认的 Bean。
举个例子:DataSourceAutoConfiguration 中会创建一个默认的 DataSource Bean,但它的定义上标注了 @ConditionalOnMissingBean(DataSource.class)。这意味着:
- 如果你在项目中没有 定义自己的
DataSource,Spring Boot 会根据application.yml中的配置自动创建一个 - 如果你在项目中定义了 自己的
DataSource,Spring Boot 的自动配置就不会覆盖它
这就是 "约定优于配置"的底层实现------框架提供默认值,但你可以随时覆盖。
常见的自动配置加载顺序控制:
当多个自动配置类之间有依赖关系时,可以通过 @AutoConfigureBefore 和 @AutoConfigureAfter 来控制加载顺序。例如,DataSourceAutoConfiguration 需要先于 JdbcTemplateAutoConfiguration 加载,因为后者依赖前者创建的数据源。
六、自定义 Starter 实战:把自动配置打包成可复用的模块
理解了自动配置的原理,我们就可以自己动手写一个 Starter 了。
Starter 的组成:
一个标准的 Spring Boot Starter 通常包含两个模块:
xxx-spring-boot-starter:依赖管理模块,负责引入所需的依赖和autoconfigure模块xxx-spring-boot-autoconfigure:自动配置模块,包含配置类和自动配置声明
开发步骤(以 Spring Boot 3.x 为例):
第一步:创建 autoconfigure 模块,编写自动配置类
java
package com.example.greeting.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GreetingService greetingService(GreetingProperties properties) {
return new GreetingService(properties.getPrefix(), properties.getSuffix());
}
}
第二步:定义配置属性类
java
package com.example.greeting.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
private String prefix = "Hello";
private String suffix = "!";
// getter / setter 省略
}
第三步:声明自动配置类(Spring Boot 3.x 方式)
在 src/main/resources/META-INF/spring/ 目录下创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件:
com.example.greeting.autoconfigure.GreetingAutoConfiguration
第四步:创建 starter 模块(可选)
如果你希望用户只引入一个依赖就能使用,可以创建一个 starter 模块,在它的 pom.xml 中引入 autoconfigure 模块:
xml
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-autoconfigure</artifactId>
</dependency>
使用方式:
其他项目引入这个 starter 后,只需要在 application.yml 中配置:
yaml
greeting:
prefix: "你好"
suffix: "~"
然后就可以通过 @Autowired 注入 GreetingService 使用了------完全不需要任何额外的配置。
代码示例
示例一:查看 Spring Boot 自动配置报告
Spring Boot 提供了一个非常有用的调试工具------自动配置报告。它告诉你哪些自动配置类生效了,哪些没有,以及没有生效的原因。
方式一:启动时添加 --debug 参数
在启动应用时添加 --debug 参数:
bash
java -jar myapp.jar --debug
或者在 IDE 中,在启动配置的 Program arguments 中添加 --debug。
方式二:在 application.yml 中配置
yaml
debug: true
启动后,控制台会输出一份详细的自动配置报告:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required classes 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)
- @ConditionalOnMissingBean (types: javax.sql.DataSource; SearchStrategy: all) did not find any beans (OnBeanCondition)
Negative matches:
-----------------
JdbcTemplateAutoConfiguration:
- @ConditionalOnClass required classes 'org.springframework.jdbc.core.JdbcTemplate' found;
- @ConditionalOnSingleCandidate (types: javax.sql.DataSource) did not find a primary bean from 'dataSource' (OnBeanCondition)
Positive matches :自动配置生效的类及其原因
Negative matches :自动配置未生效的类及其原因
这份报告是排查自动配置问题的第一手工具。如果你引入了一个 Starter 但它的功能没有生效,先看这份报告------它会告诉你哪个条件不满足。
示例二:自定义一个简单的 Starter
我们来完整实现一个"问候服务"的 Starter。
项目结构:
greeting-spring-boot-starter/
├── greeting-spring-boot-autoconfigure/
│ ├── src/main/java/com/example/greeting/autoconfigure/
│ │ ├── GreetingService.java
│ │ ├── GreetingProperties.java
│ │ └── GreetingAutoConfiguration.java
│ └── src/main/resources/META-INF/spring/
│ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── greeting-spring-boot-starter/
└── pom.xml
1. GreetingService(业务类) :
java
package com.example.greeting.autoconfigure;
public class GreetingService {
private final String prefix;
private final String suffix;
public GreetingService(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
public String greet(String name) {
return prefix + " " + name + suffix;
}
}
2. GreetingProperties(配置属性类) :
java
package com.example.greeting.autoconfigure;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
private String prefix = "Hello";
private String suffix = "!";
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; }
}
3. GreetingAutoConfiguration(自动配置类) :
java
package com.example.greeting.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GreetingService greetingService(GreetingProperties properties) {
return new GreetingService(properties.getPrefix(), properties.getSuffix());
}
}
4. 声明自动配置类(Spring Boot 3.x 方式) :
在 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中写入:
com.example.greeting.autoconfigure.GreetingAutoConfiguration
5. starter 模块的 pom.xml :
xml
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-spring-boot-autoconfigure</artifactId>
<version>1.0.0</version>
</dependency>
使用方:
引入 starter 后,在 application.yml 中配置:
yaml
greeting:
prefix: "你好"
suffix: "~"
在代码中注入使用:
java
@RestController
public class HelloController {
@Autowired
private GreetingService greetingService;
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return greetingService.greet(name); // 输出:你好 张三~
}
}
关键观察:
@ConditionalOnMissingBean保证了如果使用方自己定义了GreetingService的 Bean,自动配置不会覆盖@EnableConfigurationProperties让application.yml中的greeting.*配置自动绑定到GreetingProperties对象- 使用方零配置即可使用------引入依赖、注入 Bean、直接调用
新手错误 vs 正确姿势
| 错误表象 | 根本原因 | 正确姿势 |
|---|---|---|
| 引入依赖后自动配置未生效,功能没有加载 | 自动配置的条件不满足(如缺少某个类、某个 Bean 已存在、某个配置属性不匹配) | 启动时添加 --debug 查看自动配置报告,找到 Negative matches 中对应的原因 |
| 自定义的配置被 Spring Boot 默认配置覆盖了 | 未使用 @ConditionalOnMissingBean 保护自定义 Bean |
在 @Bean 方法上添加 @ConditionalOnMissingBean,让自动配置仅在用户未定义时才生效 |
| Spring Boot 3.x 项目中自定义 Starter 的自动配置不生效 | 仍然使用 META-INF/spring.factories 声明自动配置类 |
Spring Boot 3.x 必须使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件 |
@ComponentScan 扫描不到某些包中的 Bean |
@SpringBootApplication 默认只扫描启动类所在包及其子包 |
使用 @ComponentScan(basePackages = {"com.example.module1", "com.example.module2"}) 显式指定扫描范围 |
| 在 Spring Boot 2.x 和 3.x 之间切换时,自动配置行为不一致 | 两个版本的自动配置加载机制不同(spring.factories vs .imports) |
确认项目使用的 Spring Boot 版本,按对应版本的规范配置 |
疑难深度追问
Q1:spring.factories 和 AutoConfiguration.imports 有什么区别?
spring.factories 是 Spring Boot 2.x 及之前版本使用的通用配置文件 ,采用 key-value 格式,一个文件同时用于自动配置、监听器、初始化器等多种 SPI 扩展点。AutoConfiguration.imports 是 Spring Boot 3.x 引入的专用配置文件 ,每行一个配置类全限定名,职责更单一。Spring Boot 3.x 中,spring.factories 中的 EnableAutoConfiguration 条目不再被读取。
Q2:如果多个自动配置类有冲突,Spring Boot 如何决定加载顺序?
通过三个注解控制:
@AutoConfigureBefore:指定在某个配置类之前加载@AutoConfigureAfter:指定在某个配置类之后加载@AutoConfigureOrder:指定加载顺序的优先级(数字越小越先加载)
Spring Boot 在加载自动配置类时,会解析这些注解,对配置类进行排序后再依次加载。
Q3:为什么 Spring Boot 的自动配置要使用 @ConditionalOnMissingBean?这个设计有什么好处?
@ConditionalOnMissingBean 实现了 "可覆盖的默认值" 设计模式。它带来的好处:
- 灵活性:开发者可以完全控制 Bean 的创建逻辑,不被框架的默认实现绑定
- 渐进式学习:新手可以完全依赖自动配置(开箱即用),高手可以精细覆盖(按需定制)
- 模块化 :多个 Starter 可以共存,通过
@ConditionalOnMissingBean避免 Bean 重复定义导致冲突
思考与延伸
-
动手验证 :在 Spring Boot 项目中设置
debug: true,启动后查看自动配置报告,找到 3 个 Positive matches 和 3 个 Negative matches,理解它们生效/不生效的原因。 -
思考题 :
@ConditionalOnMissingBean和@Primary都能解决"多个同类型 Bean 冲突"的问题,它们的使用场景有什么不同? -
延伸阅读 :Spring Boot 官方文档的 "Developing Auto-configuration" 章节对自定义 Starter 有完整的指南。另外,
AutoConfigurationImportSelector的源码是理解自动配置加载流程的最佳入口。
参考与延伸阅读
- Spring Boot. Developing Auto-configuration and Using Conditions. Spring Boot Documentation
- Spring Boot. Auto-configuration. Spring Boot Documentation
- 腾讯云. Spring Boot自动配置深度解析:@EnableAutoConfiguration与AutoConfigurationImportSelector源码解密. 2025-08-27
- 阿里云. SpringBoot自动装配机制. 2025-12-11
- 腾讯云. 什么是约定优于配置?自动配置的原理是什么?. 2025-12-18
- 阿里云. SpringBoot自动配置的原理是什么?. 2025-07-15
- 阿里云. SpringBoot中如何自定义starter. 2025-12-12
- 腾讯云. Spring Boot Starter 自定义开发:封装中间件配置. 2026-02-19