1. 概述

本文将深入解析 Spring 中一个非常实用但容易被忽视的注解:@ConditionalOnProperty

你可能在写自动配置或需要根据配置动态加载 Bean 时遇到过这种需求:只有当某个配置项存在且值满足条件时,才注册某个 Bean。这时候 @ConditionalOnProperty 就派上用场了。

我们将从基本用法讲起,再到实际场景中的高级配置,帮你彻底掌握这个“条件开关”,避免踩坑。

2. @ConditionalOnProperty 的作用

在 Spring(尤其是 Spring Boot)开发中,我们经常需要根据配置文件中的属性来决定是否创建某个 Bean。比如:

  • 开发环境用 Mock 服务,生产环境用真实服务
  • 是否启用某个功能模块(如短信通知、邮件服务)
  • 切换数据源(测试库 vs 生产库)

@ConditionalOnProperty 正是为此设计的——它是一个条件注解,用于控制 Bean 的注册时机。

它的核心逻辑是:只有当指定的配置属性存在,并且其值符合条件时,被标注的 Bean 才会被注入到 Spring 容器中

默认情况下:

  • 属性必须存在
  • 值不能为 false(即 trueyeson1 等会被视为 true)

⚠️ 注意:它属于 Spring Boot 的条件化配置机制的一部分,位于 spring-boot-autoconfigure 模块中,所以确保你的项目引入了相关依赖。

3. 实际使用示例

我们通过一个简单的通知系统来演示它的用法。

3.1 定义接口与实现

先定义一个通知发送接口:

public interface NotificationSender {
    String send(String message);
}

然后实现邮件通知:

public class EmailNotification implements NotificationSender {
    @Override
    public String send(String message) {
        return "Email Notification: " + message;
    }
}

3.2 使用 @ConditionalOnProperty 控制 Bean 加载

我们现在希望:只有当配置了 notification.service 属性时,才加载 EmailNotification 这个 Bean

配置如下:

@Configuration
public class NotificationConfig {

    @Bean(name = "emailNotification")
    @ConditionalOnProperty(prefix = "notification", name = "service")
    public NotificationSender notificationSender() {
        return new EmailNotification();
    }
}

📌 关键参数说明:

  • prefix = "notification":配置前缀
  • name = "service":属性名
  • 合起来就是检查 notification.service 是否存在

3.3 配置 application.properties

application.properties 中添加:

notification.service=email

此时,EmailNotification Bean 就会被成功加载。

❌ 如果你不配置这个属性,或者设置为 notification.service=(空值),该 Bean 不会注册。

4. 高级用法与技巧

4.1 根据属性值精确匹配 —— havingValue

上面的例子只判断属性是否存在。但很多时候我们需要根据具体值来决定加载哪个实现。

比如我们现在加一个短信通知:

public class SmsNotification implements NotificationSender {
    @Override
    public String send(String message) {
        return "SMS Notification: " + message;
    }
}

我们希望:

  • notification.service=email → 加载 EmailNotification
  • notification.service=sms → 加载 SmsNotification

这时就需要用到 havingValue 属性:

@Bean(name = "smsNotification")
@ConditionalOnProperty(
    prefix = "notification", 
    name = "service", 
    havingValue = "sms"
)
public NotificationSender notificationSender2() {
    return new SmsNotification();
}

✅ 效果:只有当 notification.service=sms 时,SmsNotification 才会被加载。

而之前的 EmailNotification 只检查属性是否存在,因此只要不是 false,都会加载。这可能会导致两个 Bean 同时存在。

4.2 如何避免多个 Bean 冲突?

简单粗暴的方式是:也让 Email 的加载也加上 havingValue 判断:

@Bean(name = "emailNotification")
@ConditionalOnProperty(
    prefix = "notification", 
    name = "service", 
    havingValue = "email"
)
public NotificationSender emailNotification() {
    return new EmailNotification();
}

这样就能实现互斥加载,保证只有一个通知服务被启用。

4.3 处理属性缺失的情况 —— matchIfMissing

有时候我们希望:如果用户没配这个属性,默认启用某个功能

这就用到 matchIfMissing 参数:

@Bean(name = "emailNotification")
@ConditionalOnProperty(
    prefix = "notification", 
    name = "service", 
    havingValue = "email",
    matchIfMissing = true
)
public NotificationSender emailNotification() {
    return new EmailNotification();
}

matchIfMissing = true 表示:即使 notification.service 没有配置,也认为条件成立。

⚠️ 注意:matchIfMissinghavingValue 一起使用时要小心。如果属性不存在,havingValue 的比较不会生效,而是直接通过(因为条件“缺失即匹配”)。

4.4 多属性支持 —— name 数组

你还可以同时检查多个属性:

@ConditionalOnProperty(
    prefix = "notification", 
    name = {"service", "enabled"}, 
    havingValue = "true"
)

表示:notification.service=truenotification.enabled=true 才加载。

4.5 测试验证

下面是一个典型的测试用例,验证配置生效:

@Test
public void whenValueSetToEmail_thenCreateEmailNotification() {
    this.contextRunner
        .withPropertyValues("notification.service=email")
        .withUserConfiguration(NotificationConfig.class)
        .run(context -> {
            assertThat(context).hasBean("emailNotification");
            NotificationSender sender = context.getBean(EmailNotification.class);
            assertThat(sender.send("Hello From Baeldung!"))
                .isEqualTo("Email Notification: Hello From Baeldung!");
            assertThat(context).doesNotHaveBean("smsNotification");
        });
}

✅ 该测试模拟了 Spring 上下文启动,设置属性并验证 Bean 是否正确加载。

5. 总结

@ConditionalOnProperty 是 Spring Boot 条件化配置中非常实用的工具,适合用于:

  • 功能开关控制
  • 多实现类的条件加载
  • 自动配置模块的精细化控制

📌 核心要点回顾:

特性 说明
prefix + name 指定要检查的配置项,如 app.feature
havingValue 要求属性值必须等于指定字符串
matchIfMissing 属性不存在时是否默认匹配(可用于设默认开启)
name 支持 可同时检查多个属性

✅ 推荐使用场景:

  • 自定义 Starter 中的自动配置
  • 多环境差异化 Bean 注册
  • 第三方服务可选集成(如是否启用 Redis 缓存)

❌ 避免滥用:

  • 不要用它替代 @Profile(环境隔离还是用 Profile 更清晰)
  • 不要嵌套太多条件导致逻辑复杂难维护

源码已上传至 GitHub:https://github.com/baeldung/tutorials/tree/master/spring-boot-modules/spring-boot-autoconfiguration


原始标题:The Spring @ConditionalOnProperty Annotation | Baeldung