乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Boot 概述

    • 章节导读:Spring Boot概述与核心理念
    • Spring Boot是什么
    • Spring Boot与Spring Framework的关系
    • 约定优于配置
  • 第2章 快速入门与第一个应用

    • 章节导读:快速入门与第一个应用
    • SpringApplication
    • 第一个Spring Boot应用
  • 第3章 起步依赖与版本管理

    • 章节导读:起步依赖与版本管理
    • 起步依赖
    • BOM版本管理
  • 第4章 自动配置原理

    • 章节导读:自动配置原理
    • 自动配置原理
    • AutoConfigurationImportSelector
    • 自动配置报告
  • 第5章 核心注解

    • 章节导读:核心注解
    • @SpringBootApplication
    • @EnableAutoConfiguration
    • @ConditionalOnClass
    • @ConditionalOnMissingBean
    • @ConditionalOnBean
    • @ConditionalOnProperty
  • 第6章 外部化配置与属性绑定

    • 章节导读:外部化配置与属性绑定
    • 外部化配置
    • @ConfigurationProperties
    • @Value
    • 配置属性优先级
  • 第7章 Profile 与环境切换

    • 章节导读:Profile与环境切换
    • @Profile
    • 多环境配置文件
  • 第8章 内嵌服务器与部署

    • 章节导读:内嵌服务器与部署
    • 内嵌服务器
    • Fat Jar
  • 第9章 Actuator 与监控

    • 章节导读:Actuator与监控
    • Actuator Health
    • Actuator Info
    • Actuator Metrics
    • 自定义Endpoint
    • 自定义HealthIndicator
  • 第10章 开发工具与最佳实践

    • 章节导读:开发工具与最佳实践
    • Banner自定义
    • 热部署

一句话定位:@ConditionalOnProperty 是 Spring Boot 自动配置的配置属性开关,它根据 application.yml(或 application.properties)中的指定属性值来决定是否启用某配置类或 Bean,实现"配置驱动行为"——无需改代码,只改配置即可控制功能启停。


定义与作用

@ConditionalOnProperty 是 Spring Boot 条件注解家族中最灵活的一员。它不像 @ConditionalOnClass 那样只能检查类路径,也不像 @ConditionalOnMissingBean 那样只能检查容器状态——它检查的是外部配置属性,让开发者通过修改 YAML 文件就能控制应用行为。

它的典型应用场景包括:

场景说明示例
功能开关通过配置控制某个功能是否启用是否启用短信发送、是否开启性能监控
环境差异化不同环境使用不同的配置策略开发环境启用 Mock,生产环境启用真实服务
模块热插拔在不修改代码的情况下禁用某个模块禁用缓存模块、禁用定时任务

对比传统 Spring:在传统 SSM 中,要实现"功能开关"通常需要定义布尔值 Bean 并在业务代码中判断,或者使用 @Profile 但只能按环境区分。@ConditionalOnProperty 提供了更细粒度的、基于任意属性值的控制。


适用位置与常用属性

适用位置

@ConditionalOnProperty 可以标注在:

  1. 类级别:控制整个配置类是否生效
  2. @Bean 方法级别:控制单个 Bean 是否注册
// 类级别:整个功能模块受控
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "feixiang.sms", name = "enabled", havingValue = "true")
public class SmsAutoConfiguration { ... }

// 方法级别:单个 Bean 受控
@Configuration
public class FeatureConfig {
    
    @Bean
    @ConditionalOnProperty(prefix = "feixiang.audit", name = "enabled", havingValue = "true")
    public AuditLogger auditLogger() {
        return new AuditLogger();
    }
}

常用属性

属性类型必填说明示例
prefixString否属性前缀,会自动追加 .prefix = "feixiang.cache"
nameString[]是属性名(或数组),与 prefix 拼接成完整属性名name = "enabled" → feixiang.cache.enabled
havingValueString否属性必须等于的值,条件才匹配havingValue = "true"
matchIfMissingboolean否当属性不存在时,是否默认匹配matchIfMissing = false(默认)

属性拼接规则:prefix + . + name = 完整属性名。如果 prefix 为空,则直接使用 name。


核心原理

配置属性读取与匹配机制

配置属性来源与优先级

关键理解:@ConditionalOnProperty 读取的是 Environment 中的最终属性值,这意味着它自动继承 Spring Boot 的 17 级配置优先级——命令行参数可以覆盖 YAML 文件,环境变量可以覆盖系统属性。这为不同环境(dev/test/prod)控制功能开关提供了极大灵活性。


完整示例

场景简述

飞翔科技(广州)的学生成绩管理系统 com.feixiang.student 需要实现一个成绩导出功能。架构师白歌要求:该功能默认关闭(避免生产环境意外导出大量数据),但可以通过 application.yml 开启;同时支持 csv 和 excel 两种导出格式,通过配置属性切换。

小崔需要设计一个配置类,让导出功能的启用和格式都通过外部配置控制。

操作前:代码中硬编码开关

// 操作前:错误示范,功能开关硬编码在代码中
package com.feixiang.student.service;

import org.springframework.stereotype.Service;

@Service
public class ExportService {
    
    private static final boolean EXPORT_ENABLED = true;  // ← 硬编码!
    private static final String EXPORT_FORMAT = "csv";   // ← 硬编码!
    
    public String exportStudentScores() {
        if (!EXPORT_ENABLED) {
            return "导出功能已禁用";
        }
        if ("csv".equals(EXPORT_FORMAT)) {
            return "导出 CSV 格式的学生成绩(学号2024001等)";
        } else {
            return "导出 Excel 格式的学生成绩";
        }
    }
}

后果:每次需要切换导出格式或关闭导出功能时,小崔必须修改代码、重新编译、重新打包、重新部署。飞翔科技的生产环境禁止导出功能,但小崔忘记改代码,导致上线后数据泄露风险。白歌在代码审查时发现了这个问题。

使用该注解的完整代码

小崔改用 @ConditionalOnProperty 实现配置驱动:

步骤 1:创建配置属性类

package com.feixiang.student.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "feixiang.export")
public class ExportProperties {
    
    private boolean enabled = false;  // 默认关闭
    private String format = "csv";     // 默认 CSV
    
    // getter / setter
    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public String getFormat() { return format; }
    public void setFormat(String format) { this.format = format; }
}

步骤 2:创建条件化的导出配置类

package com.feixiang.student.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 成绩导出功能自动配置类
 * 只有当 feixiang.export.enabled=true 时才生效
 * 
 * @author 小崔
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(
    prefix = "feixiang.export",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = false  // 默认不启用,必须显式开启
)
public class ExportAutoConfiguration {
    
    @Bean
    @ConditionalOnProperty(prefix = "feixiang.export", name = "format", havingValue = "csv")
    public Exporter csvExporter(ExportProperties properties) {
        return new CsvExporter(properties);
    }
    
    @Bean
    @ConditionalOnProperty(prefix = "feixiang.export", name = "format", havingValue = "excel")
    public Exporter excelExporter(ExportProperties properties) {
        return new ExcelExporter(properties);
    }
}

步骤 3:定义导出器接口与实现

package com.feixiang.student.config;

public interface Exporter {
    String export(Long studentId);
}

public class CsvExporter implements Exporter {
    private final ExportProperties properties;
    
    public CsvExporter(ExportProperties properties) {
        this.properties = properties;
    }
    
    @Override
    public String export(Long studentId) {
        return "CSV 导出:学生 " + studentId + " 的成绩记录";
    }
}

public class ExcelExporter implements Exporter {
    private final ExportProperties properties;
    
    public ExcelExporter(ExportProperties properties) {
        this.properties = properties;
    }
    
    @Override
    public String export(Long studentId) {
        return "Excel 导出:学生 " + studentId + " 的成绩记录";
    }
}

步骤 4:在 application.yml 中配置

# application.yml
feixiang:
  export:
    enabled: true
    format: csv

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/student_db
    username: root
    password: secret

步骤 5:业务代码中注入

package com.feixiang.student.service;

import com.feixiang.student.config.Exporter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class StudentService {
    
    private final Exporter exporter;
    
    @Autowired
    public StudentService(@Autowired(required = false) Exporter exporter) {
        this.exporter = exporter;
    }
    
    public String exportScore(Long studentId) {
        if (exporter == null) {
            return "导出功能未启用";
        }
        return exporter.export(studentId);
    }
}

操作后运行结果及分析

场景 A:启用 CSV 导出

feixiang:
  export:
    enabled: true
    format: csv

启动日志:

2024-05-20 15:00:12.345  INFO 12345 --- [main] c.f.s.c.ExportAutoConfiguration : 
    ExportAutoConfiguration matched: 
    @ConditionalOnProperty matched feixiang.export.enabled=true
2024-05-20 15:00:12.567  INFO 12345 --- [main] c.f.s.c.ExportAutoConfiguration : 
    CsvExporter bean registered

场景 B:启用 Excel 导出

feixiang:
  export:
    enabled: true
    format: excel

启动日志:

2024-05-20 15:05:22.789  INFO 12345 --- [main] c.f.s.c.ExportAutoConfiguration : 
    ExportAutoConfiguration matched: feixiang.export.enabled=true
2024-05-20 15:05:22.901  INFO 12345 --- [main] c.f.s.c.ExportAutoConfiguration : 
    ExcelExporter bean registered

场景 C:禁用导出(默认)

feixiang:
  export:
    enabled: false

或不配置该属性(matchIfMissing = false)

启动日志:

2024-05-20 15:10:33.123  INFO 12345 --- [main] o.s.b.a.c.AutoConfigurationReport : 
    ExportAutoConfiguration:
      Did not match:
         - @ConditionalOnProperty (feixiang.export.enabled=true) did not find property 'enabled'
2024-05-20 15:10:33.456  INFO 12345 --- [main] c.f.s.StudentManagementApplication : 
    Started StudentManagementApplication in 1.234 seconds

分析:

  1. 功能开关:@ConditionalOnProperty 将 feixiang.export.enabled 作为总开关。matchIfMissing = false 确保默认情况下功能关闭,必须显式配置 enabled: true 才能开启。
  2. 格式切换:CsvExporter 和 ExcelExporter 分别由各自的 @ConditionalOnProperty 控制,根据 feixiang.export.format 的值决定注册哪一个。由于两者是互斥的 havingValue,不会同时注册。
  3. 安全设计:生产环境只要不配置 feixiang.export.enabled: true,导出功能模块就完全不会加载,避免了数据导出风险。

易错场景与面试考点

易错场景一:havingValue 与属性值类型不匹配

# application.yml
feixiang:
  cache:
    enabled: true  # 布尔值
// 错误示范:havingValue 写成字符串 "1"
@Configuration
@ConditionalOnProperty(
    prefix = "feixiang.cache",
    name = "enabled",
    havingValue = "1"  // ← 错误!YAML 中是 true,不是 "1"
)
public class CacheAutoConfiguration { ... }

后果:@ConditionalOnProperty 是按字符串比较的。Environment.getProperty() 读取到的值是字符串 "true",而 havingValue 是 "1",两者不相等,条件不匹配,CacheAutoConfiguration 被跳过。小崔排查半天发现 havingValue 写错了。

正确做法:havingValue 必须与配置文件中的值严格一致。YAML 的布尔值 true 对应字符串 "true":

@ConditionalOnProperty(
    prefix = "feixiang.cache",
    name = "enabled",
    havingValue = "true"  // 正确:与 YAML 中的 true 对应
)

易错场景二:matchIfMissing = true 导致意外的默认开启

// 错误示范:默认开启,但开发者误以为默认关闭
@Configuration
@ConditionalOnProperty(
    prefix = "feixiang.monitoring",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = true  // ← 危险!属性不存在时默认开启
)
public class MonitoringAutoConfiguration { ... }

后果:小崔在 application.yml 中没有配置 feixiang.monitoring.enabled,但由于 matchIfMissing = true,MonitoringAutoConfiguration 仍然被加载。生产环境中这个监控模块消耗了大量资源,但小崔以为它是关闭的。

正确做法:对于可能消耗资源或涉及安全的功能,应使用 matchIfMissing = false(默认),要求显式开启:

@ConditionalOnProperty(
    prefix = "feixiang.monitoring",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = false  // 安全:默认关闭
)

面试考点

Q:@ConditionalOnProperty 的 havingValue 和 matchIfMissing 如何配合使用?

havingValue 指定属性必须等于的值(字符串精确匹配);matchIfMissing 指定当属性完全不存在时条件的默认行为。常见组合:

  • 默认关闭(推荐):havingValue = "true",matchIfMissing = false → 必须显式写 enabled: true 才开启
  • 默认开启:havingValue = "true",matchIfMissing = true → 不配置时默认开启,配置 enabled: false 时关闭
  • 严格匹配(无默认值):havingValue = "prod" → 只有 spring.profiles.active=prod 时生效

Q:@ConditionalOnProperty 可以检查多个属性吗?

可以。name 属性是 String[] 数组,可以同时指定多个属性名。此时要求所有属性都满足条件(AND 关系)。例如 @ConditionalOnProperty(prefix = "app", name = {"feature-a", "feature-b"}, havingValue = "true") 要求 app.feature-a=true 且 app.feature-b=true 同时成立。

Q:@ConditionalOnProperty 与 Spring 的 @Profile 有什么区别?

@Profile 基于激活的 Profile(如 dev/test/prod)做条件判断,粒度较粗,只能按环境区分;@ConditionalOnProperty 基于任意配置属性做判断,粒度更细,可以按功能开关、按配置值、按业务标识等任意维度控制。两者可以结合使用:@Profile("prod") + @ConditionalOnProperty(...) 表示"只在生产环境且属性满足时才生效"。

Q:@ConditionalOnProperty 读取的是最终合并后的属性值吗?

是的。它通过 Environment.getProperty() 读取,自动继承 Spring Boot 的 17 级配置优先级。这意味着命令行参数 --feixiang.export.enabled=true 可以覆盖 application.yml 中的 feixiang.export.enabled: false,让运维人员在启动时临时开启功能,无需修改配置文件。

上一页
@ConditionalOnBean