条件注解
条件注解让 Bean 或配置类只在满足特定条件时才注册到容器,是 Spring Boot 自动配置的核心机制。
核心接口
// 所有条件注解的底层实现
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
// ConditionContext 提供:BeanDefinitionRegistry、BeanFactory、Environment、
// ResourceLoader、ClassLoader 等上下文信息Spring Boot 内置条件注解
@ConditionalOnProperty(最常用)
根据配置文件中的属性值决定是否装配:
// 只有 app.cache.enabled=true 时才注册
@Bean
@ConditionalOnProperty(prefix = "app.cache", name = "enabled", havingValue = "true")
public CacheManager redisCacheManager() { ... }
// 属性不存在时也装配(matchIfMissing 默认 false)
@Bean
@ConditionalOnProperty(
prefix = "app.mail",
name = "enabled",
havingValue = "true",
matchIfMissing = true // 未配置时视为 true,默认装配
)
public MailService mailService() { ... }
// 属性值不等于 false 时装配(常用于功能开关)
@ConditionalOnProperty(name = "feature.new-dashboard", matchIfMissing = true)
@Configuration
public class NewDashboardConfig { ... }@ConditionalOnClass / @ConditionalOnMissingClass
类路径上存在/不存在某个类时装配,常用于 Starter 兼容多种可选依赖:
// 类路径有 Redis 客户端才注册 Redis 缓存
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
public class RedisCacheAutoConfiguration { ... }
// 没有 Jackson 时使用 Gson
@Bean
@ConditionalOnMissingClass("com.fasterxml.jackson.databind.ObjectMapper")
public Gson gson() { return new Gson(); }
// 同时检查多个类(AND 关系)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })@ConditionalOnBean / @ConditionalOnMissingBean
容器中存在/不存在某个 Bean 时装配:
// 容器中已有 DataSource 才配置 JdbcTemplate
@Bean
@ConditionalOnBean(DataSource.class)
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds);
}
// 容器中没有 PasswordEncoder 时提供默认实现
@Bean
@ConditionalOnMissingBean(PasswordEncoder.class)
public PasswordEncoder defaultPasswordEncoder() {
return new BCryptPasswordEncoder();
}
// 按名称查找 Bean
@ConditionalOnBean(name = "dataSource")
// 按注解查找 Bean(容器中有带该注解的 Bean)
@ConditionalOnBean(annotation = EnableCaching.class)
@ConditionalOnMissingBean是编写 Starter 的核心模式:先提供默认实现,用户自定义 Bean 后自动退出。
@ConditionalOnExpression
使用 SpEL 表达式,支持复杂组合条件:
// 同时满足两个配置
@ConditionalOnExpression(
"${app.feature.enabled:true} and '${app.env}' != 'test'"
)
public class FeatureConfig { ... }
// Bean 存在且属性为特定值
@ConditionalOnExpression(
"#{@redisConnectionFactory != null && '${app.cache.type}' == 'redis'}"
)
public class RedisCacheConfig { ... }@ConditionalOnWebApplication / @ConditionalOnNotWebApplication
// 仅在 Web 环境(有 Servlet 容器)时装配
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class WebMvcConfig { ... }
// 仅在响应式 Web 环境时装配
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class WebFluxConfig { ... }
// 非 Web 环境(如命令行工具、批处理)
@Configuration
@ConditionalOnNotWebApplication
public class BatchConfig { ... }@ConditionalOnResource
类路径或文件系统上存在特定资源时装配:
// 存在 logback-spring.xml 时跳过默认日志配置
@ConditionalOnResource(resources = "classpath:logback-spring.xml")
// 存在外部文件时加载
@ConditionalOnResource(resources = "file:/opt/app/config/custom.yml")@ConditionalOnJava
根据 JVM 版本装配:
// Java 21+ 才使用虚拟线程
@Bean
@ConditionalOnJava(JavaVersion.TWENTY_ONE)
public Executor virtualThreadExecutor() {
return Executors.newVirtualThreadPerTaskExecutor();
}
// Java 21 以下使用普通线程池
@Bean
@ConditionalOnJava(value = JavaVersion.TWENTY_ONE,
range = ConditionalOnJava.Range.OLDER_THAN)
public Executor threadPoolExecutor() { ... }注解汇总
| 注解 | 条件 |
|---|---|
@ConditionalOnProperty | 配置属性匹配 |
@ConditionalOnClass | 类路径存在某类 |
@ConditionalOnMissingClass | 类路径不存在某类 |
@ConditionalOnBean | 容器中存在某 Bean |
@ConditionalOnMissingBean | 容器中不存在某 Bean |
@ConditionalOnExpression | SpEL 表达式为 true |
@ConditionalOnWebApplication | 当前是 Web 应用 |
@ConditionalOnNotWebApplication | 当前不是 Web 应用 |
@ConditionalOnResource | 资源文件存在 |
@ConditionalOnJava | JVM 版本匹配 |
@ConditionalOnSingleCandidate | 容器中只有一个该类型的 Bean |
@Profile | 激活的 Profile 匹配(本质也是 Conditional) |
自定义条件注解
// 1. 实现 Condition
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getProperty("os.name")
.toLowerCase().contains("linux");
}
}
// 2. 组合为注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnLinuxCondition.class)
public @interface ConditionalOnLinux {}
// 3. 使用
@Bean
@ConditionalOnLinux
public FileWatcher linuxFileWatcher() { ... }带参数的自定义条件:
// 根据数据库类型条件装配
public class OnDatabaseTypeCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attrs =
metadata.getAnnotationAttributes(ConditionalOnDatabaseType.class.getName());
String requiredType = (String) attrs.get("value");
String actualType = context.getEnvironment()
.getProperty("spring.datasource.type", "");
return actualType.contains(requiredType);
}
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(OnDatabaseTypeCondition.class)
public @interface ConditionalOnDatabaseType {
String value();
}
// 使用:MySQL 时装配 MySQL 专用方言
@Bean
@ConditionalOnDatabaseType("mysql")
public SqlDialect mySqlDialect() { ... }在自动配置中的典型模式
Spring Boot Starter 的标准写法——先检查类存在,再检查 Bean 不存在,用户自定义优先:
@AutoConfiguration
@ConditionalOnClass(RedisConnectionFactory.class) // 有 Redis 依赖
@ConditionalOnProperty(name = "spring.data.redis.host") // 有 Redis 配置
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
// 用户没有自定义 RedisTemplate 时才提供默认实现
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
// 用户没有自定义 StringRedisTemplate 时才提供
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
}自动配置机制详见 自动配置,自定义 Starter 详见 自定义Starter。
条件评估顺序
多个条件注解叠加时为 AND 关系,全部满足才装配:
@Bean
@ConditionalOnClass(RedissonClient.class) // 条件 1
@ConditionalOnBean(RedisConnectionFactory.class) // 条件 2
@ConditionalOnProperty(name = "app.lock.type",
havingValue = "redisson") // 条件 3
public RedissonDistributedLock redissonLock() { ... }
// 三个条件都满足才注册此 Bean条件注解在 BeanDefinition 注册阶段评估,此时其他 Bean 可能尚未创建。@ConditionalOnBean 依赖 Bean 注册顺序,若目标 Bean 尚未注册会误判为不存在,建议搭配 @AutoConfiguration(after = ...) 控制顺序。
相关链接
- 自动配置 — Spring Boot 自动配置如何批量应用条件注解
- 自定义Starter — 编写 Starter 时
@ConditionalOnMissingBean的标准用法 - 属性绑定 —
@ConditionalOnProperty中属性的来源与优先级 - 环境与Profile —
@Profile是@Conditional的封装 - Bean生命周期 — 条件注解在 BeanDefinition 注册阶段的评估时机
- IOC与DI — BeanFactory 与条件 Bean 的注册机制