Spring Boot 最引人注目的特性之一就是其**自动配置(Auto-Configuration)**机制。它让开发者能够在几乎零配置的情况下快速启动一个功能完善的 Spring 应用。本文将深入剖析自动配置的底层原理,重点讲解 @Conditional 条件注解和 spring.factories 加载机制,并通过实际案例展示如何开发自定义 Starter。
Spring Boot 的设计理念是**"约定优于配置"(Convention Over Configuration)**。传统 Spring 应用需要大量的 XML 或 Java 配置来定义 Bean,而 Spring Boot 通过智能推断,自动为你配置好最常用的组件。
// 传统 Spring 需要这样配置 DataSource
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setUrl("jdbc:mysql://localhost:3306/mydb");
ds.setUsername("root");
ds.setPassword("password");
return ds;
}
// Spring Boot 只需要在 application.yml 中配置
// spring.datasource.url=jdbc:mysql://localhost:3306/mydb
// 自动配置会根据 classpath 中的依赖自动创建 DataSource
Spring Boot 的自动配置并非无条件生效,它基于以下因素进行智能判断:
application.properties 或 application.yml 中的配置项@Conditional 注解定义复杂条件@Conditional 是 Spring 4.0 引入的注解,用于根据特定条件决定是否创建 Bean 或加载配置类。Spring Boot 在此基础上扩展了大量实用的条件注解。
| 注解 | 作用 | 示例场景 |
|---|---|---|
@ConditionalOnClass | 类路径存在指定类时生效 | 检测到 MySQL 驱动时配置数据源 |
@ConditionalOnMissingClass | 类路径不存在指定类时生效 | 无 Redis 时配置本地缓存 |
@ConditionalOnBean | 容器中存在指定 Bean 时生效 | 有 DataSource 时配置 JdbcTemplate |
@ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 | 用户未自定义时提供默认实现 |
@ConditionalOnProperty | 指定属性满足条件时生效 | 开关控制功能启用/禁用 |
@ConditionalOnWebApplication | 是 Web 应用时生效 | Web 环境特有的配置 |
@ConditionalOnExpression | SpEL 表达式为 true 时生效 | 复杂逻辑判断 |
@Configuration
@ConditionalOnClass(name = "com.mysql.cj.jdbc.Driver")
public class MySQLAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 用户已自定义时不覆盖
public DataSource mysqlDataSource(DataSourceProperties properties) {
return DataSourceBuilder.create()
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword())
.driverClassName("com.mysql.cj.jdbc.Driver")
.build();
}
}
@Configuration
@ConditionalOnProperty(
prefix = "app.cache", // 前缀
name = "enabled", // 属性名
havingValue = "true", // 期望值
matchIfMissing = true // 属性不存在时默认启用
)
public class CacheAutoConfiguration {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
对应的 application.yml:
app:
cache:
enabled: true
ttl: 3600
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(RedisOperations.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
public class RedisSessionAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "springSessionDefaultRedisSerializer")
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
当内置条件无法满足需求时,可以实现 Condition 接口创建自定义条件。
// 自定义条件:检查是否在云环境中运行
public class OnCloudEnvironmentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 检查环境变量或系统属性
String cloudProvider = System.getenv("CLOUD_PROVIDER");
return StringUtils.hasText(cloudProvider);
}
}
// 创建组合注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnCloudEnvironmentCondition.class)
public @interface ConditionalOnCloud {
String value() default ""; // 可指定特定云厂商
}
// 使用自定义条件
@Configuration
@ConditionalOnCloud("aws")
public class AWSAutoConfiguration {
// AWS 特有的配置
}
spring.factories 是 Spring Boot 自动配置的核心入口,它定义了所有需要自动配置的类。
spring.factories 文件位于 META-INF/ 目录下,采用 Properties 格式:
# META-INF/spring.factories
# 自动配置类列表(Spring Boot 2.7+ 推荐使用新的 imports 文件)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration,\
com.example.starter.AnotherAutoConfiguration
# 其他 SPI 扩展点
org.springframework.context.ApplicationContextInitializer=\
com.example.starter.MyContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.example.starter.MyEnvironmentPostProcessor
Spring Boot 启动时会调用 SpringFactoriesLoader.loadFactories() 方法:
// SpringApplication.run() 内部调用
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class,
getClass().getClassLoader()
);
加载流程:
META-INF/spring.factories 文件EnableAutoConfiguration 键对应的类名列表@AutoConfigureOrder 和 @AutoConfigureAfter 排序@Conditional 注解过滤不满足条件的配置类Spring Boot 2.7 引入了新的加载方式,推荐在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中列出自动配置类:
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.starter.MyAutoConfiguration
com.example.starter.AnotherAutoConfiguration
新方式的优点:
my-spring-boot-starter/
├── pom.xml
└── src/
└── main/
├── java/
│ └── com/example/starter/
│ ├── MyAutoConfiguration.java # 自动配置类
│ ├── MyProperties.java # 配置属性类
│ ├── MyService.java # 核心服务类
│ └── MyServiceBuilder.java # 构建器
└── resources/
└── META-INF/
├── spring.factories # 旧版配置
└── spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports # 新版配置
@ConfigurationProperties(prefix = "my.starter")
public class MyProperties {
private boolean enabled = true;
private String endpoint = "https://api.example.com";
private int timeout = 5000;
private Map<String, String> headers = new HashMap<>();
// getters and setters
}
public class MyService {
private final String endpoint;
private final int timeout;
public MyService(String endpoint, int timeout) {
this.endpoint = endpoint;
this.timeout = timeout;
}
public String execute(String request) {
// 执行业务逻辑
return "Result of " + request;
}
}
@Configuration
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "my.starter", name = "enabled", matchIfMissing = true)
public class MyAutoConfiguration {
private final MyProperties properties;
public MyAutoConfiguration(MyProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService(
properties.getEndpoint(),
properties.getTimeout()
);
}
@Bean
@ConditionalOnBean(MyService.class)
public MyServiceHealthIndicator myHealthIndicator(MyService service) {
return new MyServiceHealthIndicator(service);
}
}
创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports:
com.example.starter.MyAutoConfiguration
为了提供更好的 IDE 自动提示,添加配置元数据:
{
"groups": [
{
"name": "my.starter",
"type": "com.example.starter.MyProperties",
"sourceType": "com.example.starter.MyProperties"
}
],
"properties": [
{
"name": "my.starter.enabled",
"type": "java.lang.Boolean",
"description": "是否启用 My Starter",
"defaultValue": true
},
{
"name": "my.starter.endpoint",
"type": "java.lang.String",
"description": "API 端点地址"
},
{
"name": "my.starter.timeout",
"type": "java.lang.Integer",
"description": "请求超时时间(毫秒)",
"defaultValue": 5000
}
]
}
在其他项目中引入:
<dependency>
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
配置使用:
my:
starter:
endpoint: https://custom-api.example.com
timeout: 10000
注入使用:
@Service
public class BusinessService {
@Autowired
private MyService myService;
public void doSomething() {
String result = myService.execute("request");
// ...
}
}
启动时添加 --debug 参数或在配置中启用:
debug: true
控制台将输出详细的自动配置报告:
Positive matches:(生效的配置)
-----------------
MyAutoConfiguration matched:
- @ConditionalOnClass found required class 'com.example.MyService' (OnClassCondition)
- @ConditionalOnProperty (my.starter.enabled=true) matched (OnPropertyCondition)
Negative matches:(未生效的配置)
-----------------
RedisAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)
@SpringBootApplication(
exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
}
)
public class Application {
// ...
}
或使用配置:
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
@ConditionalOnMissingBean:允许用户覆盖默认配置,提供灵活性mycompany.feature@AutoConfigureAfter 控制配置加载顺序Spring Boot 的自动配置机制是其生态系统成功的关键。通过 @Conditional 条件注解和 spring.factories 加载机制,开发者可以构建高度可插拔、零配置的组件。掌握这些原理不仅能帮助你更好地使用 Spring Boot,还能让你开发出高质量的自定义 Starter,为团队和社区贡献价值。
理解自动配置的底层原理,是成为 Spring Boot 高级开发者的必经之路。希望本文能帮助你深入理解这一核心机制。