@ConditionalOnProperty 注解

#Spring #注解 #配置 #条件装配

@ConditionalOnProperty 注解详解

@ConditionalOnProperty 是 Spring Boot 的条件注解,用于根据配置文件中的属性值控制 Bean 是否被创建。它检查指定属性是否存在、值是否匹配,从而决定是否启用相关配置。(符合条件就创建Bean)

工作原理

  • 检查逻辑:注解会检查配置文件(如 application.yamlapplication.properties)中的属性值。
  • 条件判断
    • 如果属性值(name| value) 等于 havingValue(或默认匹配),则条件为真,Bean 被创建。
    • 否则为假,Bean 不被创建。
  • 结果影响
    • 为真:Bean 被实例化,配置生效。
    • 为假:Bean 被跳过,配置无效。

主要参数

参数 说明 默认值
name / value 配置文件中的属性名(从 application.yml 读取) -
havingValue 期望的属性值,只有匹配时才创建 Bean ""(空字符串,表示只要属性存在即可)
matchIfMissing 属性不存在时是否默认匹配(true = 创建,false = 不创建) false
prefix 属性名前缀,用于简化配置(如 prefix="app" + name="enabled"app.enabled ""

参数组合示例:

// 场景1: 属性必须存在且值为 "true"
@ConditionalOnProperty(name = "feature.enabled", havingValue = "true")
// 配置: feature.enabled=true ✅ | feature.enabled=false ❌ | 不存在 ❌

// 场景2: 属性存在即可(值不限)
@ConditionalOnProperty(name = "feature.enabled")
// 配置: feature.enabled=true ✅ | feature.enabled=false ✅ | 不存在 ❌

// 场景3: 属性不存在时默认创建
@ConditionalOnProperty(name = "feature.enabled", matchIfMissing = true)
// 配置: feature.enabled=true ✅ | feature.enabled=false ❌ | 不存在 ✅

基础示例

假设 application.yaml 配置如下:

app:
  logging:
    enabled: true  # 可改为 false 或删除测试
@Configuration
public class AppConfig {

    @Bean
    @ConditionalOnProperty(name = "app.logging.enabled", havingValue = "true")
    public LoggingService loggingService() {
        return new LoggingService();
    }
}
// 如果 app.logging.enabled=true → LoggingService 被创建
// 如果为 false 或不存在 → 不创建

实际业务场景

场景1:短信服务降级开关

需求:生产环境关闭短信发送功能,或切换到模拟实现(避免扣费)。

配置文件 application.yml

service:
  sms:
    enabled: false
    mock: false          # true 则使用模拟实现(不实际发送)

实现代码

@Configuration
public class SmsConfig {

    // 真实短信服务(阿里云/腾讯云)
    @Bean
    @ConditionalOnProperty(name = "service.sms.enabled", havingValue = "true", matchIfMissing = false)
    public SmsService realSmsService() {
        return new AliyunSmsService(); // 或 TencentSmsService
    }

    // 模拟短信服务(开发/测试用)
    @Bean
    @ConditionalOnProperty(name = "service.sms.enabled", havingValue = "false", matchIfMissing = true)
    public SmsService mockSmsService() {
        return new MockSmsService(); // 只打印日志,不发送
    }
}

效果

  • 生产环境关闭 enabled → 自动切换到模拟实现,避免意外扣费
  • 开发/测试环境开启 → 真实发送

场景2:多数据源动态切换(读写分离)

需求:根据配置选择使用主库(写)或从库(读)的数据源。

配置文件

datasource:
  mode: master  # master=主库 | slave=从库 | cluster=集群模式

实现代码

@Configuration
public class DataSourceConfig {

    // 主数据源(写库)
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.master")
    @ConditionalOnProperty(name = "datasource.mode", havingValue = "master", matchIfMissing = true)
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 从数据源(读库)
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    @ConditionalOnProperty(name = "datasource.mode", havingValue = "slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 集群模式(负载均衡)
    @Bean
    @ConditionalOnProperty(name = "datasource.mode", havingValue = "cluster")
    public DataSource clusterDataSource() {
        return new ClusterDataSource(); // 自定义实现
    }
}

效果

  • 开发环境使用 mode: master → 单库开发
  • 生产环境使用 mode: slave → 读写分离
  • 高并发场景使用 mode: cluster → 多库负载均衡

场景3:功能灰度发布(新功能开关)

需求:新功能上线时先对部分用户开放,通过配置控制是否启用。

配置文件

feature:
  new-ui:
    enabled: true      # 是否启用新UI
    whitelist: "user1,user2,admin"  # 白名单用户

实现代码

@Configuration
public class FeatureConfig {

    // 新版UI服务
    @Bean
    @ConditionalOnProperty(name = "feature.new-ui.enabled", havingValue = "true")
    public UiService newUiService() {
        return new NewUiService();
    }

    // 旧版UI服务(降级方案)
    @Bean
    @ConditionalOnProperty(name = "feature.new-ui.enabled", havingValue = "false", matchIfMissing = true)
    public UiService legacyUiService() {
        return new LegacyUiService();
    }
}

// 使用示例
@Service
public class UserService {

    @Autowired
    private UiService uiService;  // 根据配置自动注入新旧实现

    public String getHomePage(String userId) {
        return uiService.renderHomePage(userId);
    }
}

效果

  • 灰度期间 enabled: false → 所有用户用旧版
  • 逐步放开 enabled: true → 部分用户用新版
  • 出问题立即回滚 → 改为 false 即可

常见问题

Q1: matchIfMissing 的使用场景?

  • 设为 true:默认启用的功能(如开发环境默认开启日志)
  • 设为 false:需要显式配置才能启用(如生产环境的监控功能)

Q2: 与 @Value 注解的区别?

特性 @ConditionalOnProperty @Value
作用范围 类/方法级别(控制 Bean 创建) 字段/参数/方法级别(注入值)
决策时机 应用启动时(只判断一次) 每次访问时(动态读取)
典型场景 功能开关、数据源切换 配置参数注入

Q3: 如何实现多条件组合?

使用 @ConditionalOnExpression(SpEL 表达式):

@ConditionalOnExpression("'${service.sms.enabled}' == 'true' and '${service.sms.mock}' == 'false'")
public SmsService realSmsService() { ... }

Reference