Spring Boot 自动配置原理与自定义 Starter 实战
“为什么引入一个 starter 依赖,什么都不配置就能用?”、“Redis、DataSource 这些 Bean 到底是谁创建的?”——答案都指向 Spring Boot 的自动配置(Auto Configuration)机制。本文从启动注解拆起,讲清自动配置的加载链路与条件装配,并手把手实现一个自定义 Starter。
一、@SpringBootApplication 拆解
这个“三合一”组合注解由三个核心注解组成:
@SpringBootConfiguration // 等价于 @Configuration:标记为配置类
@EnableAutoConfiguration // 自动配置的总开关(核心)
@ComponentScan // 扫描主类所在包及其子包的 @Component/@Service/@Repository/@Controller
public class Application { ... }
注意 @ComponentScan 默认只扫主类所在包及其子包——这也是“为什么 controller 放错包就不被识别”的原因。
二、自动配置的原理:AutoConfigurationImportSelector
自动配置的入口是 @EnableAutoConfiguration,它通过 @Import(AutoConfigurationImportSelector.class) 引入了选择器。Spring 容器启动时,选择器会调用 getCandidateConfigurations():
// 简化逻辑:从 classpath 读取自动配置类的候选列表
protected List<String> getCandidateConfigurations(...) {
// Spring Boot 2.7 之后从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 读取
// Spring Boot 2.6 及以前读取 META-INF/spring.factories 中 key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
List<String> configurations = ...;
return configurations;
}
加载链路一句话:@EnableAutoConfiguration → AutoConfigurationImportSelector → 读取 classpath 下所有 jar 里的自动配置类列表 → 逐个按 @Conditional 条件判断 → 满足条件的配置类生效,注册相关 Bean。
这意味着每个 starter jar 里都“藏”着自动配置类,但是否真正创建 Bean,取决于条件注解是否命中——例如没有引入 Redis 相关类,RedisAutoConfiguration 的条件就不满足,不会注册任何多余 Bean。
三、条件装配:@Conditional 家族
条件注解决定了配置类“装还是不装”,开发中高频使用的有:
| 注解 | 生效条件 | 典型用途 |
|---|---|---|
| @ConditionalOnClass / OnMissingClass | classpath 存在 / 不存在某类 | 按依赖判断(如引入 redis 客户端才配置) |
| @ConditionalOnBean / OnMissingBean | 容器存在 / 不存在某 Bean | 用户自定义了同名 Bean 就“让位”(尊重覆盖) |
| @ConditionalOnProperty | 配置项存在且值匹配 | 开关控制,如 xxx.enabled=true 才生效 |
| @ConditionalOnWebApplication | 当前是 Web 环境 | 区分 MVC / WebFlux |
| @ConditionalOnMissingBean(name=...) | 缺失指定名称的 Bean | 提供默认实现、允许用户覆盖 |
// 典型的自动配置类骨架(以自定义 demo 为例)
@AutoConfiguration // Spring Boot 2.7+ 推荐的注册注解
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "demo", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HelloService helloService(DemoProperties props) {
return new HelloService(props.getPrefix());
}
}
四、手写一个自定义 Starter
Starter 通常包含两个模块:自动配置模块(autoconfigure,含配置类)和 Starter 模块(仅声明对 autoconfigure 的依赖)。实际中常合并为单个模块,下面给出最小可用实现。
第 1 步:创建模块并编写配置属性类
// DemoProperties.java —— 把 application.yml 里的配置映射成类
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
private String prefix = "Hello"; // 默认值
private String suffix = "!";
// getter / setter 略
}
第 2 步:编写业务类与自动配置类
// HelloService.java —— 对外提供的“服务”
public class HelloService {
private final DemoProperties props;
public HelloService(DemoProperties props) { this.props = props; }
public String hello(String name) {
return props.getPrefix() + ", " + name + props.getSuffix();
}
}
// DemoAutoConfiguration.java
@AutoConfiguration
@ConditionalOnClass(HelloService.class)
@EnableConfigurationProperties(DemoProperties.class)
@ConditionalOnProperty(prefix = "demo", name = "enabled", matchIfMissing = true)
public class DemoAutoConfiguration {
@Bean
@ConditionalOnMissingBean(HelloService.class)
public HelloService helloService(DemoProperties props) {
return new HelloService(props);
}
}
第 3 步:注册自动配置类(关键一步,容易被漏)
# 在 resources/META-INF/ 下新建(Spring Boot 2.7+)
# 文件:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.demo.autoconfigure.DemoAutoConfiguration
# —— 若使用 Spring Boot 2.6 及以前,则写进 spring.factories ——
# org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
# com.example.demo.autoconfigure.DemoAutoConfiguration
第 4 步:业务项目引入并测试
// 引入依赖 demo-spring-boot-starter 后,直接注入即可使用
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping("/hello")
public String hello(@RequestParam String name) {
return helloService.hello(name); // 输出: Hi, Bob!
}
}
# application.yml 中可覆盖默认前缀
demo:
prefix: Hi
# enabled: false # 关闭此配置类
五、开发中常见问题
1. 想看哪些自动配置生效了? 在 application.yml 加 debug: true,启动日志会输出 “Positive matches”(生效的)和 “Negative matches”(未生效及原因),排查“为什么没生效”非常有用。
2. 想排除某个自动配置? 两种方式:@SpringBootApplication(exclude = DataSourceAutoConfiguration.class),或在配置文件里 spring.autoconfigure.exclude=... 。
3. 为什么我自定义的 Bean 被自动配置的覆盖了? 自动配置类大多用 @ConditionalOnMissingBean 兜底,你自己 @Bean/@Component 注册的同名 Bean 会优先生效——这正是它“可被用户覆盖”的设计。
4. 多个自动配置有依赖顺序怎么办? 用 @AutoConfigureBefore / @AutoConfigureAfter / @AutoConfigureOrder 控制先后(G1 场景如先配连接池再配 MyBatis)。
六、小结
自动配置的底层三件套是:配置文件列表(imports/spring.factories)+ 条件注解(@Conditional 系列)+ 配置属性绑定(@ConfigurationProperties)。看懂这一条链路,不仅能解释“为什么 starter 即插即用”,写中间件、做组件封装时也能自己“造一个 starter”。理解它,你就真正跨过了 Spring Boot“会用”到“懂原理”的门槛。