自动配置是 Spring Boot 最核心、也最区别于普通 Spring 的东西。用 Spring Boot 时什么都没手动配,Redis、MyBatis、WebMVC 引入依赖就能直接用,背后就是自动配置在起作用。这篇把自动配置的原理拆开:@SpringBootApplication 背后是什么、条件注解怎么工作、怎么自己写一个自动配置。
想先了解"为什么会有 Spring、为什么后来又要有 Spring Boot"的历史背景,看《Spring 与 Spring Boot 的来历》。
一、为什么需要自动配置
先看普通 Spring 和 Spring Boot 的区别:
- 普通 Spring:集成 Redis 要自己写
RedisTemplate、RedisConnectionFactory一堆 Bean;集成 MyBatis 要自己配SqlSessionFactory、DataSource、Mapper 扫描……每个中间件都配一遍,重复又易错。 - Spring Boot:引入
spring-boot-starter-data-redis,配置文件里写个host/port,RedisTemplate就直接能用了——因为你没写的那些 Bean,自动配置帮你建好了。
这就是自动配置的价值:把"每个中间件都要手动配一遍"的重复劳动,变成"引入依赖 + 写配置就能用"。
二、@SpringBootApplication 背后的三件事
每个 Spring Boot 主类上都有 @SpringBootApplication,它其实是三个注解的合体:
@SpringBootConfiguration // 等价于 @Configuration,标记这是配置类
@EnableAutoConfiguration // 核心:开启自动配置的开关
@ComponentScan // 扫描当前包下的组件
public @interface SpringBootApplication {}其中 @EnableAutoConfiguration 是自动配置的总开关,它内部又做了两件事:
@AutoConfigurationPackage // 记录主类所在包,用于默认包扫描
@Import(AutoConfigurationImportSelector.class) // 关键:导入自动配置选择器
public @interface EnableAutoConfiguration {}AutoConfigurationImportSelector 就是自动配置的"发动机"——它负责在启动时把该加载的自动配置类全部找出来、加载进来。
三、自动配置怎么"自动":条件注解
自动配置类不是无条件生效的,它们靠条件注解决定"要不要生效"。核心几个:
| 注解 | 含义 |
|---|---|
@ConditionalOnClass | 类路径上有某个类才生效 |
@ConditionalOnMissingClass | 类路径上没有某个类才生效 |
@ConditionalOnBean | 容器里有某个 Bean 才生效 |
@ConditionalOnMissingBean | 容器里没有某个 Bean 才生效 |
@ConditionalOnProperty | 配置项满足条件才生效 |
@ConditionalOnWebApplication | 是 Web 应用才生效 |
拿 RedisAutoConfiguration 举例(我之前写的《Redis 快速入门》里 Spring Boot 集成 Redis 就是它的功劳):
@ConditionalOnClass(RedisOperations.class) // 有 Redis 相关类才加载
@EnableConfigurationProperties(RedisProperties.class) // 绑定 spring.data.redis.* 配置
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") // 用户没自己定义才自动建
public RedisTemplate<Object, Object> redisTemplate(...) { ... }
}两个条件配合出的效果:你引入了 Redis 依赖(有类),又没自己定义 RedisTemplate(缺 Bean),它才帮你自动建一个。一旦你自己写了个 RedisTemplate,它就自动让位——这就是"约定优于配置"的精髓。
四、自动配置类怎么被发现的
那么多 starter,Spring Boot 怎么知道要加载哪些自动配置类?靠一个注册文件:
- 旧版(2.7 之前):
META-INF/spring.factories里的EnableAutoConfiguration键。 - 新版(2.7+):
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,每行一个自动配置类的全限定名。
AutoConfigurationImportSelector 启动时读取所有 jar 里的这个文件,把自动配置类全列出来,再用条件注解逐个判断要不要加载。所以引入一个 starter,本质就是引入了它自带的自动配置类清单。
五、自己写一个自动配置(自定义 starter 实战)
理解了原理,就能自己写一个 starter。以"一个打招呼的服务"为例:
1. 配置属性类
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
private boolean enabled = true;
private String name = "World";
// getter / setter 省略
}2. 自动配置类
@Configuration
@ConditionalOnClass(GreetingService.class) // 有 GreetingService 才加载
@EnableConfigurationProperties(GreetingProperties.class) // 绑定 greeting.* 配置
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户没定义才建
public GreetingService greetingService(GreetingProperties props) {
return new GreetingService(props.isEnabled(), props.getName());
}
}3. 注册文件 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
com.example.GreetingAutoConfiguration4. 依赖 spring-boot-autoconfigure。
别人引入你的 starter 后,写一句 greeting.name=波哥,GreetingService 就自动注入可用了——和官方 starter 一个体验。
六、几个要注意的点
@ConditionalOnMissingBean只在当前配置类内部生效:它只检查"目前扫描到的地方有没有这个 Bean",不能跨配置类保证唯一,多个自动配置类都想建同名 Bean 时顺序就很重要。- 自动配置类的顺序:用
@AutoConfigureBefore/@AutoConfigureAfter控制加载先后(比如自定义配置要在某个官方配置之后生效)。 - 条件注解按声明顺序判断:同一个类上多个
@ConditionalOnXxx是从上到下依次判断,都满足才生效。 - 别在自动配置里做太重的事:自动配置应该只负责"建 Bean",业务逻辑放业务代码里,保持 starter 干净、可预测。
小结
- 自动配置的价值:把"重复手动配 Bean"变成"引入依赖 + 写配置就能用"
@SpringBootApplication=@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan@EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class)触发自动配置加载- 自动配置靠条件注解(
@ConditionalOnClass/@ConditionalOnMissingBean/@ConditionalOnProperty)决定是否生效 - 自动配置类通过
AutoConfiguration.imports文件注册被发现 - 自定义 starter 四步:属性类 + 自动配置类 + 注册文件 +
spring-boot-autoconfigure依赖
想了解 Spring Boot 具体集成了哪些中间件、它们怎么用,看我整理的《Redis 快速入门》《RabbitMQ 快速入门》——这些文章里的 Spring Boot 集成代码,背后都是对应的 AutoConfiguration 在起作用。
