Spring条件注解机制
约 1472 字大约 5 分钟
springconditional
2025-04-08
概述
Spring 条件注解机制是 Spring Boot 自动配置的核心基础。通过 @Conditional 及其派生注解,Spring 可以根据运行时环境(类路径、Bean 存在性、配置属性等)动态决定是否注册某个 Bean 或加载某个配置类。理解条件注解机制,是掌握 Spring Boot 自动配置原理的关键。
条件注解体系
@Conditional —— 基础条件注解
@Conditional 是所有条件注解的基础,接受一个实现了 Condition 接口的类作为条件判断逻辑。
// 自定义 Condition
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String os = context.getEnvironment().getProperty("os.name", "");
return os.toLowerCase().contains("linux");
}
}
public class OnWindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String os = context.getEnvironment().getProperty("os.name", "");
return os.toLowerCase().contains("windows");
}
}
// 使用自定义条件
@Configuration
public class FileServiceConfig {
@Bean
@Conditional(OnLinuxCondition.class)
public FileService linuxFileService() {
return new LinuxFileService();
}
@Bean
@Conditional(OnWindowsCondition.class)
public FileService windowsFileService() {
return new WindowsFileService();
}
}常用条件注解详解
@ConditionalOnClass / @ConditionalOnMissingClass
当类路径中存在(或不存在)某个类时,条件成立。
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
// 当没有 MongoDB 驱动时,不加载 MongoDB 配置
@Configuration
@ConditionalOnClass(name = "com.mongodb.client.MongoClient")
public class MongoAutoConfiguration {
// ...
}@ConditionalOnBean / @ConditionalOnMissingBean
当容器中存在(或不存在)某个 Bean 时,条件成立。
@Configuration
public class DataSourceAutoConfiguration {
// 只有当用户没有自定义 DataSource 时才创建默认的
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource defaultDataSource() {
return DataSourceBuilder.create()
.url("jdbc:h2:mem:testdb")
.build();
}
// 只有当 DataSource Bean 存在时才创建 JdbcTemplate
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}@ConditionalOnProperty
根据配置属性值决定是否加载。
// 基本用法:属性存在且值为 true
@Configuration
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
public class CacheConfig {
// ...
}
// matchIfMissing:属性不存在时的默认行为
@Configuration
@ConditionalOnProperty(prefix = "app.security", name = "enabled",
havingValue = "true", matchIfMissing = true)
public class SecurityConfig {
// 默认启用安全配置(即使没有显式配置 app.security.enabled)
}
// 多属性条件
@Bean
@ConditionalOnProperty(prefix = "app.mail", name = {"host", "port"})
public MailSender mailSender() {
return new SmtpMailSender();
}对应配置:
app:
cache:
enabled: true
security:
enabled: true # 不配置也会启用(matchIfMissing=true)
mail:
host: smtp.example.com
port: 587@ConditionalOnResource
当指定资源存在时,条件成立。
@Configuration
@ConditionalOnResource(resources = "classpath:custom-config.xml")
public class CustomXmlConfig {
// 只有当类路径中存在 custom-config.xml 时才加载
}@ConditionalOnWebApplication / @ConditionalOnNotWebApplication
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ServletWebConfig {
// 仅在 Servlet Web 应用中加载
}
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveWebConfig {
// 仅在 Reactive Web 应用中加载
}
@Configuration
@ConditionalOnNotWebApplication
public class NonWebConfig {
// 仅在非 Web 应用中加载(如命令行工具)
}@ConditionalOnExpression
基于 SpEL 表达式进行条件判断。
@Bean
@ConditionalOnExpression(
"${app.feature.enabled:false} and '${app.env}'.equals('production')")
public FeatureToggle productionFeature() {
return new ProductionFeatureToggle();
}
@Bean
@ConditionalOnExpression("#{environment.getProperty('app.mode') == 'cluster'}")
public ClusterManager clusterManager() {
return new ClusterManager();
}@ConditionalOnJava
根据 Java 版本判断。
@Bean
@ConditionalOnJava(JavaVersion.SEVENTEEN)
public VirtualThreadExecutor virtualThreadExecutor() {
return new VirtualThreadExecutor();
}条件注解组合使用
@AutoConfiguration
@ConditionalOnClass(DataSource.class) // 1. 类路径有DataSource
@ConditionalOnProperty(prefix = "spring.datasource", // 2. 配置了数据源URL
name = "url")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 3. 用户未自定义
@ConditionalOnProperty(prefix = "spring.datasource",
name = "type",
havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true) // 4. 默认使用HikariCP
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(properties.getUrl());
ds.setUsername(properties.getUsername());
ds.setPassword(properties.getPassword());
return ds;
}
}自定义条件注解
创建组合条件注解
// 自定义注解:当 Redis 可用时生效
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ConditionalOnClass(RedisConnectionFactory.class)
@ConditionalOnProperty(prefix = "spring.redis", name = "host")
public @interface ConditionalOnRedisAvailable {
}
// 使用
@Configuration
@ConditionalOnRedisAvailable
public class RedisCacheConfig {
// 只有当 Redis 依赖存在且配置了 host 时才加载
}完整自定义 Condition
// 自定义条件:检查数据库连通性
public class DatabaseAvailableCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String url = context.getEnvironment().getProperty("spring.datasource.url");
if (url == null) {
return ConditionOutcome.noMatch("No datasource URL configured");
}
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return ConditionOutcome.match("MySQL driver available");
} catch (ClassNotFoundException e) {
return ConditionOutcome.noMatch("MySQL driver not found");
}
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(DatabaseAvailableCondition.class)
public @interface ConditionalOnDatabaseAvailable {
}AutoConfigureOrder 和加载顺序
// 控制自动配置类的加载顺序
@AutoConfiguration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 最先加载
public class EarlyAutoConfiguration {
// ...
}
// 在某个自动配置之后加载
@AutoConfiguration(after = DataSourceAutoConfiguration.class)
public class MyJdbcAutoConfiguration {
// 确保 DataSource 已经配置好
}
// 在某个自动配置之前加载
@AutoConfiguration(before = WebMvcAutoConfiguration.class)
public class CustomWebConfig {
// ...
}调试条件注解
# 查看条件评估报告
debug: true
# 或
logging.level.org.springframework.boot.autoconfigure: DEBUG启动日志中会输出:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found required class 'javax.sql.DataSource'
- @ConditionalOnProperty (spring.datasource.url) matched
Negative matches:
-----------------
MongoAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'com.mongodb.client.MongoClient'也可以通过 Actuator 端点查看:
GET /actuator/conditions条件注解在自动配置中的核心作用
总结
Spring 条件注解机制是 Spring Boot "约定优于配置"理念的技术基础。通过 @ConditionalOnClass 检测依赖、@ConditionalOnProperty 读取配置、@ConditionalOnMissingBean 实现退让策略,自动配置类可以智能地根据运行时环境决定是否生效。开发自定义 Starter 时,合理使用条件注解可以让自动配置既灵活又不强侵入,用户始终可以通过自定义 Bean 覆盖默认行为。
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于