Spring Boot框架的自动配置机制通过条件注解(Conditional)实现”约定优于配置”,开发者引入Starter依赖后,框架自动装配Bean而无需手动编写XML。Spring Boot 3基于Jakarta EE 9+规范,自动配置底层从spring.factories迁移到AutoConfiguration.imports。本文实战讲解条件注解的原理与自定义Starter开发全流程。
Spring Boot自动配置加载机制与AutoConfiguration.imports
Spring Boot启动时,SpringApplication.run()触发自动配置流程。AutoConfigurationImportSelector读取META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,加载其中声明的所有自动配置类。每个配置类通过条件注解决定是否生效。
// Spring Boot 3自动配置类示例
@AutoConfiguration
@ConditionalOnClass(DataSource.class)
@ConditionalOnProperty(prefix = "app.datasource",
name = "enabled", havingValue = "true",
matchIfMissing = true)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource dataSource(DataSourceProperties props) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(props.getUrl());
ds.setUsername(props.getUsername());
ds.setPassword(props.getPassword());
ds.setMaximumPoolSize(props.getPoolSize());
return ds;
}
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
}
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件内容:
com.example.autoconfigure.DataSourceAutoConfiguration
com.example.autoconfigure.WebClientAutoConfiguration
com.example.autoconfigure.CacheAutoConfiguration
核心条件注解分类与组合使用
Spring Boot提供四类条件注解,可组合使用实现精确的装配控制:
| 注解 | 触发条件 | 典型场景 |
|---|---|---|
| ConditionalOnClass | 类路径存在指定类 | 依赖存在才装配 |
| ConditionalOnMissingBean | 容器中不存在指定Bean | 允许用户覆盖默认配置 |
| ConditionalOnProperty | 配置属性满足条件 | 开关控制装配 |
| ConditionalOnBean | 容器中存在指定Bean | 依赖Bean存在才装配 |
| ConditionalOnWebApplication | 是Web应用 | 仅Web环境生效 |
| ConditionalOnExpression | SpEL表达式为true | 复杂条件组合 |
// 自定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnLinuxCondition.class)
public @interface ConditionalOnLinux {
}
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String os = System.getProperty("os.name");
return os != null && os.toLowerCase().contains("linux");
}
}
// 组合条件注解
@Configuration
@ConditionalOnLinux
@ConditionalOnProperty(name = "app.feature.enabled",
havingValue = "true")
@ConditionalOnClass(RedisTemplate.class)
public class LinuxRedisFeatureConfiguration {
@Bean
@ConditionalOnMissingBean
public FeatureService featureService(RedisTemplate redis) {
return new RedisFeatureService(redis);
}
}
// 条件嵌套: @ConditionalOnExpression处理复杂逻辑
@Bean
@ConditionalOnExpression(
"'${app.env}' == 'prod' && ${app.cache.ttl} > 0 " +
"&& T(org.springframework.util.StringUtils)" +
".hasText('${app.cache.region}')"
)
public CacheManager productionCacheManager() {
return new RedisCacheManager();
}
自定义Starter组件完整开发流程
以下开发一个短信服务Starter,包含自动配置、属性绑定和条件装配:
项目结构:
sms-spring-boot-starter/
├── pom.xml
└── src/main/
├── java/com/example/sms/
│ ├── SmsProperties.java
│ ├── SmsService.java
│ ├── SmsAutoConfiguration.java
│ └── SmsTemplate.java
└── resources/
└── META-INF/spring/
└── org.springframework.boot.autoconfigure
.AutoConfiguration.imports
// 1. 属性配置类
@ConfigurationProperties(prefix = "sms")
public class SmsProperties {
private String apiUrl;
private String apiKey;
private String apiSecret;
private String signName;
private int connectTimeout = 5000;
private int readTimeout = 10000;
private boolean enabled = true;
// getter/setter省略
}
// 2. 核心服务类
public class SmsService {
private final SmsProperties properties;
private final RestTemplate restTemplate;
public SmsService(SmsProperties properties) {
this.properties = properties;
this.restTemplate = new RestTemplateBuilder()
.setConnectTimeout(Duration.ofMillis(
properties.getConnectTimeout()))
.setReadTimeout(Duration.ofMillis(
properties.getReadTimeout()))
.build();
}
public SmsResult send(String phone, String templateCode,
Map<String, String> params) {
SmsRequest request = new SmsRequest();
request.setPhone(phone);
request.setTemplateCode(templateCode);
request.setParams(params);
request.setSignName(properties.getSignName());
request.setTimestamp(System.currentTimeMillis());
request.setSignature(generateSignature(request));
ResponseEntity<SmsResult> response = restTemplate.postForEntity(
properties.getApiUrl() + "/sms/send",
request, SmsResult.class
);
return response.getBody();
}
private String generateSignature(SmsRequest request) {
String raw = properties.getApiKey()
+ properties.getApiSecret()
+ request.getTimestamp();
return DigestUtils.md5Hex(raw);
}
}
// 3. 自动配置类
@AutoConfiguration
@ConditionalOnProperty(prefix = "sms", name = "enabled",
havingValue = "true", matchIfMissing = true)
@ConditionalOnClass(RestTemplate.class)
@EnableConfigurationProperties(SmsProperties.class)
public class SmsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(SmsProperties.class)
public SmsService smsService(SmsProperties properties) {
return new SmsService(properties);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(SmsService.class)
public SmsTemplate smsTemplate(SmsService smsService) {
return new SmsTemplate(smsService);
}
}
// 4. AutoConfiguration.imports文件内容
// com.example.sms.SmsAutoConfiguration
业务项目引入Starter后,application.yml配置即可使用:
sms:
enabled: true
api-url: https://sms-api.example.com
api-key: AKID_xxxx
api-secret: SECRET_xxxx
sign-name: 云技
connect-timeout: 5000
read-timeout: 10000
# 使用时注入
@Autowired
private SmsTemplate smsTemplate;
smsTemplate.send("13800138000", "VERIFY_CODE",
Map.of("code", "123456"));
自动配置调试与排除排查
启动应用时添加–debug参数可打印自动配置报告,显示哪些配置类匹配、哪些被排除及原因:
java -jar app.jar --debug
# 输出示例:
# Positive matches: 实际生效的自动配置
# DataSourceAutoConfiguration matched:
# - @ConditionalOnClass found DataSource (OnClassCondition)
# - @ConditionalOnProperty 'app.datasource.enabled' matched
#
# Negative matches: 未生效的配置及原因
# RedisAutoConfiguration:
# - @ConditionalOnClass did not find RedisTemplate
#
# Unconditional classes: 无条件加载的配置
微服务架构中,自定义Starter配合服务治理(如Nacos配置中心动态刷新)可实现配置热更新。@RefreshScope注解搭配Spring Cloud Config或Nacos,当配置变更时自动重建Bean,无需重启服务。高并发设计下需注意Starter中Bean的作用域——单例Bean中的状态变量非线程安全,应通过ThreadLocal或无状态设计避免竞态条件。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot3-zi-dong-pei-zhi-tiao-jian-zhu-jie-yu-starter-zu/