条件注解

条件注解让 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
@ConditionalOnExpressionSpEL 表达式为 true
@ConditionalOnWebApplication当前是 Web 应用
@ConditionalOnNotWebApplication当前不是 Web 应用
@ConditionalOnResource资源文件存在
@ConditionalOnJavaJVM 版本匹配
@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 的注册机制