十六、@ConditionalOnResource
@ConditionalOnResource 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 类路径(classpath)或文件系统中是否存在指定资源 来决定是否加载某个配置类、Bean 或自动配置组件。
16.1 核心作用
只有当指定的资源(如配置文件、数据文件、脚本等)存在时,被注解的类或方法才会生效。
这在需要"按需加载"功能模块(例如:仅当存在 schema.sql 时才初始化数据库)时非常有用。
16.2 基本用法
java
@ConditionalOnResource(resources = "classpath:my-config.properties")
public class MyConfig {
// 仅当 classpath 下存在 my-config.properties 时加载
}
支持多个资源(逻辑 AND 关系):
java
@ConditionalOnResource(resources = {
"classpath:config/app.properties",
"file:/opt/myapp/data.txt"
})
所有指定的资源都必须存在,条件才成立。
16.3 资源路径格式
Spring 使用 ResourceLoader 解析资源,支持以下前缀:
| 前缀 | 说明 | 示例 |
|---|---|---|
classpath: |
从类路径加载 | classpath:application-prod.yml |
file: |
从文件系统绝对路径加载 | file:/etc/myapp/config.json |
| 无前缀 | 默认按 classpath: 处理(但不推荐,建议显式指定) |
my-script.sql → 等价于 classpath:my-script.sql |
⚠️ 注意:相对路径(如
./data.txt)行为不确定,应避免使用。
16.4 典型应用场景
✅ 场景 1:仅当存在 SQL 脚本时初始化数据库
java
@Configuration
@ConditionalOnResource(resources = "classpath:schema.sql")
public class DatabaseInitConfig {
@Bean
public DataSourceInitializer initializer(DataSource dataSource) {
// 自动执行 schema.sql 和 data.sql
}
}
Spring Boot 内置的
DataSourceAutoConfiguration就使用了类似逻辑。
✅ 场景 2:可选的外部配置文件
java
// 仅当用户提供了自定义 banner 文件时启用
@ConditionalOnResource(resources = "file:${user.home}/.myapp/banner.txt")
public class CustomBannerConfig {
@Bean
public Banner banner() {
return new ResourceBanner(new FileSystemResource(
System.getProperty("user.home") + "/.myapp/banner.txt"));
}
}
✅ 场景 3:模块化插件机制
假设你的应用支持插件,插件需提供 plugin-definition.json:
java
@ConditionalOnResource(resources = "classpath:plugin-definition.json")
public class PluginAutoConfig {
// 加载并注册插件
}
只要 JAR 包里包含该文件,插件自动激活。
16.5.注意事项
- 资源必须"存在且可读"
不仅要路径正确,还要求应用有读取权限(尤其对file:资源)。 - 不支持通配符或目录检测
不能写classpath:configs/*.properties或classpath:mydir/。
如需检测目录,需自定义Condition。 - 多个资源是 AND 关系
所有资源都必须存在。如需 OR 逻辑,需自定义条件。 - 性能影响极小
资源检测只在启动时执行一次,使用Resource.exists()判断。 - 与 @PropertySource 配合使用需谨慎
如果先用@ConditionalOnResource检查文件存在,再用@PropertySource加载,顺序很重要(条件判断先于属性加载)。
16.6 底层原理
@ConditionalOnResource 基于 @Conditional(OnResourceCondition.class) 实现。
OnResourceCondition 会:
- 使用
ConditionContext.getResourceLoader()获取ResourceLoader - 对每个
resources路径调用resource.exists() - 全部返回
true则条件成立
16.7 对比其他条件注解
| 注解 | 判断依据 |
|---|---|
@ConditionalOnResource |
资源文件是否存在 |
@ConditionalOnProperty |
配置属性值是否匹配 |
@ConditionalOnClass |
类路径是否存在某个 类 |
@ConditionalOnBean |
Spring 容器中是否存在某个 Bean |
💡
@ConditionalOnResource关注的是文件/资源 ,而@ConditionalOnClass关注的是Java 类。
16.8 总结
@ConditionalOnResource 是实现 "资源驱动配置" 的关键注解,适用于:
- 按需初始化(如数据库脚本)
- 可选外部配置
- 插件化架构
- 环境差异化部署(如生产环境才有某些密钥文件)
通过它,你可以让 Spring Boot 应用更加灵活、安全、自适应,避免因缺少资源导致启动失败或功能异常。
十七、@ConditionalOnJndi
@ConditionalOnJndi 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 JNDI(Java Naming and Directory Interface)环境是否可用 来决定是否加载某个配置类、Bean 或自动配置组件。
17.1 核心作用
只有当应用运行在支持 JNDI 的环境中(如传统 Java EE 应用服务器:Tomcat、WebLogic、JBoss/WildFly 等),并且可以成功查找指定的 JNDI 名称时,被注解的配置才会生效。
这主要用于兼容部署在应用服务器中的场景,例如从 JNDI 获取数据源(DataSource)、JMS 连接工厂等资源。
17.2 基本用法
1. 检查 JNDI 环境是否可用(不指定具体名称)
java
@Configuration
@ConditionalOnJndi
public class JndiBasedConfig {
// 仅当 JNDI 环境可用时加载
}
此时只要
InitialContext能成功初始化(即运行在支持 JNDI 的容器中),条件就成立。
2. 检查特定 JNDI 名称是否存在
java
@Configuration
@ConditionalOnJndi("java:comp/env/jdbc/mydb")
public class DatabaseConfig {
// 仅当 JNDI 名称 java:comp/env/jdbc/mydb 可查找到时生效
}
支持多个 JNDI 名称(逻辑 OR 关系):
java
@ConditionalOnJndi({"java:comp/env/jdbc/db1", "java:comp/env/jdbc/db2"})
只要其中一个 JNDI 名称存在,条件就成立。
17.3 典型应用场景
✅ 场景 1:从 JNDI 获取 DataSource(传统企业部署)
在 WebLogic 或 Tomcat 中配置了 JNDI 数据源:
xml
<!-- Tomcat context.xml -->
<Resource name="jdbc/mydb" auth="Container" type="javax.sql.DataSource" ... />
Spring Boot 应用中:
java
@Bean
@ConditionalOnJndi("java:comp/env/jdbc/mydb")
public DataSource dataSource() throws NamingException {
InitialContext ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
}
这样,当应用部署到支持 JNDI 的服务器时自动使用 JNDI 数据源;
若打包为可执行 JAR(无 JNDI 环境),则跳过此配置,可 fallback 到 HikariCP 等内嵌数据源。
✅ 场景 2:区分云原生与传统部署
- 云原生(Spring Boot 内嵌 Tomcat)→ 无 JNDI → 使用
application.yml配置数据库 - 传统部署(WebLogic)→ 有 JNDI → 使用 JNDI 数据源
通过 @ConditionalOnJndi 实现同一套代码两种部署模式。
17.4 底层原理
@ConditionalOnJndi 基于 @Conditional(OnJndiCondition.class) 实现。
其判断逻辑如下:
- 尝试创建
javax.naming.InitialContext - 如果失败(抛出
NamingException或NoClassDefFoundError),说明 JNDI 不可用 - 如果指定了 JNDI 名称,则进一步调用
context.lookup(name)测试是否存在 - 所有指定名称中至少一个存在,或未指定名称但 JNDI 环境可用 → 条件成立
⚠️ 注意:在 Spring Boot 默认的可执行 JAR(内嵌 Tomcat)中,JNDI 默认是禁用的 ,因此
@ConditionalOnJndi通常不会生效。
17.5 注意事项
- Spring Boot 内嵌容器默认不启用 JNDI
即使使用内嵌 Tomcat,也需要显式启用 JNDI(不推荐):
java
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setUseJndi(true);
- 主要用于传统 Java EE 迁移场景
在现代云原生架构中,JNDI 已逐渐被配置中心、环境变量等替代。 - 与 @Profile 结合使用更安全
可配合 profile 区分环境:
java
@Configuration
@Profile("prod-legacy")
@ConditionalOnJndi("java:comp/env/jdbc/mydb")
public class LegacyJndiConfig { }
- 性能影响可忽略
JNDI 检测只在启动时执行一次。
17.6 与其他条件注解对比
| 注解 | 适用场景 |
|---|---|
@ConditionalOnJndi |
运行在 Java EE 应用服务器,需从 JNDI 获取资源 |
@ConditionalOnProperty |
基于配置文件开关 |
@ConditionalOnResource |
基于文件/资源是否存在 |
@ConditionalOnClass |
基于类路径是否存在某类 |
总结
@ConditionalOnJndi 是 Spring Boot 为兼容传统企业级部署环境而提供的条件注解,适用于:
- 从 JNDI 获取数据源、JMS、EJB 等资源
- 同一套代码同时支持 云原生(无 JNDI) 和 传统应用服务器(有 JNDI) 部署
- 平滑迁移老旧 Java EE 应用到 Spring Boot
虽然在现代微服务架构中使用较少,但在企业遗留系统整合或混合部署场景中仍具有重要价值。
17.7 什么是JNDI
JNDI(Java Naming and Directory Interface ,Java 命名和目录接口)是 Java 提供的一套标准 API ,用于访问命名服务(Naming Service) 和 目录服务(Directory Service) 。它的核心作用是:通过名字查找资源,实现"解耦"------应用程序不需要知道资源的具体位置或实现细节,只需通过一个逻辑名称即可获取。
17.7.1 通俗理解:JNDI 就像"电话簿"或"DNS"
- 你不需要记住朋友的手机号(IP 地址、数据库连接串等),只需查"张三"这个名字,电话簿(JNDI)就告诉你号码。
- 在企业应用中,数据库、消息队列、EJB 等资源都被注册到 JNDI 中,程序通过名字(如
java:comp/env/jdbc/mydb)去"查号",拿到实际对象。
17.7.2 JNDI 能做什么?
| 功能 | 说明 | 示例 |
|---|---|---|
| 查找资源 | 通过名字获取对象引用 | 获取数据库连接池(DataSource) |
| 绑定资源 | 将对象注册到命名系统 | 应用服务器启动时把 DataSource 绑定到 JNDI |
| 列出上下文 | 浏览命名空间结构 | 查看当前有哪些 JNDI 名称可用 |
17.7.3 典型应用场景(企业级开发)
✅ 1. 获取数据库连接(最常见)
在传统 Java EE 应用服务器(如 Tomcat、WebLogic、JBoss)中:
-
管理员在服务器上配置一个数据源(DataSource),并给它起个 JNDI 名字,比如:
textjava:comp/env/jdbc/MyDB -
开发者 在代码中通过 JNDI 查找这个数据源,而不需要硬编码数据库 URL、用户名、密码:
java
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MyDB");
Connection conn = ds.getConnection();
✅ 好处:
- 数据库配置由运维管理,代码无需修改
- 支持连接池、高可用等企业级特性
- 安全(密码不在代码或配置文件中)
✅ 2. 查找 EJB(Enterprise JavaBeans)
在 Java EE 中,远程业务组件(EJB)也通过 JNDI 发布:
java
MyService service = (MyService) ctx.lookup("java:global/myapp/MyServiceImpl");
✅ 3. 集成 LDAP 目录服务
JNDI 也支持 LDAP(轻量目录访问协议),可用于:
- 用户认证(如公司 AD 域)
- 查询组织架构
java
DirContext ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("uid=john,ou=people,dc=example,dc=com");
17.7.4 JNDI 的命名结构(Name Syntax)
JNDI 名称通常是分层的 URI 风格,例如:
java:comp/env/jdbc/mydb
java::协议(表示 Java 命名空间)comp:组件私有命名上下文env:环境条目(Environment Entries)jdbc/mydb:自定义资源名
常见前缀:
java:comp/:当前组件私有java:global/:全局可见java:jboss/(JBoss 特有)
17.7.5 JNDI 与 Spring / Spring Boot 的关系
- 传统 Java EE 应用:重度依赖 JNDI。
- Spring Boot(默认) :不使用 JNDI ,而是通过
application.properties或application.yml配置数据源等资源。 - 但 Spring Boot 仍支持 JNDI :通过
@ConditionalOnJndi等机制,允许在部署到 WebLogic/Tomcat 等服务器时自动切换到 JNDI 模式,实现"一套代码,两种部署"。
17.7.6 为什么现代应用很少提 JNDI?
- 云原生兴起:微服务、容器化(Docker/K8s)更倾向通过环境变量、配置中心管理资源。
- Spring Boot 内嵌容器:默认使用 HikariCP 等内嵌连接池,无需应用服务器。
- JNDI 配置复杂:需在应用服务器层面配置,不利于 DevOps 自动化。
🔸 但在银行、电信等传统行业,大量系统仍运行在 WebLogic/WebSphere 上,JNDI 仍是标配。
17.7.7 简单代码示例(Tomcat 中使用 JNDI)
- 在 context.xml 中定义数据源:
xml
<Context>
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test"
username="root"
password="secret" />
</Context>
- 在 web.xml 中引用:
xml
<resource-ref>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
- Java 代码中获取:
java
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mydb");
十八、@ConditionalOnCloudPlatform
@ConditionalOnCloudPlatform 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 应用程序是否运行在特定的云平台(Cloud Platform)上 来决定是否加载某个配置类、Bean 或自动配置组件。
18.1 核心作用
只有当应用部署在指定的云平台(如 Cloud Foundry、Kubernetes、Heroku 等)时,被注解的配置才会生效。
这使得开发者可以针对不同云环境提供差异化配置,例如:
- 在 Kubernetes 中使用 ConfigMap 加载配置
- 在 Cloud Foundry 中绑定服务实例
- 在本地开发时不启用云相关功能
18.2 支持的云平台(CloudPlatform 枚举)
Spring Boot 内置识别以下云平台(截至 Spring Boot 2.4+):
| 平台 | 枚举值 | 检测依据 |
|---|---|---|
| Cloud Foundry | CLOUD_FOUNDRY |
存在环境变量 VCAP_APPLICATION |
| Kubernetes | KUBERNETES |
存在文件 /var/run/secrets/kubernetes.io/serviceaccount/namespace(即 Pod 运行环境) |
| Heroku | HEROKU |
存在环境变量 DYNO |
| SAP Cloud Platform | SAP |
存在环境变量 HCLOUD 或 SAP_JAVA_BUILDPACK |
| AWS (Amazon Web Services) | AWS |
存在环境变量 EC2_HOME 或能访问 EC2 元数据服务(需网络权限) |
| Azure | AZURE |
存在环境变量 WEBSITE_INSTANCE_ID(Azure App Service) |
| Google Cloud Platform (GCP) | GCP |
存在环境变量 GOOGLE_CLOUD_PROJECT 或元数据服务可达 |
⚠️ 注意:
- Spring Boot 不会主动探测所有平台 ,而是基于环境变量、文件系统或元数据服务进行轻量判断。
- 如果未明确运行在任何已知云平台,则视为
NONE。
18.3 基本用法
1. 指定单一云平台
java
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class K8sConfig {
// 仅在 Kubernetes 中生效
}
2. 结合其他条件使用(常见)
java
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
@ConditionalOnProperty(name = "cf.service.enabled", havingValue = "true")
public MyService cfService() {
return new CloudFoundryMyService();
}
18.4 典型应用场景
✅ 场景 1:Kubernetes 特有配置
在 K8s 中,常通过 ConfigMap 或 Secret 注入配置:
java
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class K8sSecretConfig {
@Bean
public DatabaseCredentials dbCredentials() {
// 从 /etc/secrets/db-password 读取密码
String password = Files.readString(Paths.get("/etc/secrets/db-password"));
return new DatabaseCredentials("user", password);
}
}
✅ 场景 2:Cloud Foundry 服务绑定
Cloud Foundry 通过 VCAP_SERVICES 环境变量传递服务信息:
java
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CfDataSourceConfig {
@Bean
public DataSource dataSource() {
// 解析 VCAP_SERVICES 获取数据库连接信息
String vcap = System.getenv("VCAP_SERVICES");
Map<String, Object> services = parseVcap(vcap);
// ... 构建 DataSource
}
}
✅ 场景 3:禁用本地开发时的云功能
java
// 仅在非云环境(如本地)启用 mock 服务
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.NONE)
public class LocalMockConfig {
@Bean
public EmailService emailService() {
return new MockEmailService(); // 不真实发邮件
}
}
💡
CloudPlatform.NONE表示未检测到任何已知云平台(如本地 IDE 启动)。
18.5 底层原理
@ConditionalOnCloudPlatform 基于 @Conditional(OnCloudPlatformCondition.class) 实现。
Spring Boot 在启动时会通过 CloudPlatform.getActive(Environment) 方法检测当前平台,逻辑如下:
- 遍历所有已知
CloudPlatform类型 - 调用其
isActive(Environment env)方法(检查环境变量、文件等) - 返回第一个匹配的平台;若无匹配,返回
NONE
该判断只在应用启动时执行一次,性能开销极小。
18.6 注意事项
- 检测是"尽力而为"(best-effort)
不保证 100% 准确(例如 AWS 检测需访问元数据服务,可能因网络策略失败)。 - 不适用于所有云厂商
如阿里云、腾讯云等未被内置支持。若需识别,可:
- 自定义
Condition - 使用
@ConditionalOnExpression检查特定环境变量
- 与 @Profile 互补
可结合使用:
java
@Configuration
@Profile("prod")
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class ProdK8sConfig { }
- Spring Boot 3.x 变化
对云平台的支持持续增强,但核心机制不变。
18.7 总结
@ConditionalOnCloudPlatform 是 Spring Boot 云原生能力的重要组成部分,它让应用能够:
- 自动适配不同云环境
- 安全地加载平台特有配置
- 避免在本地开发时因缺少云资源而启动失败
虽然在简单项目中可能用不到,但在混合云、多云部署或企业级 PaaS 迁移场景中,它是实现"一次构建,随处运行"的关键工具之一。
十九、@ConditionalOnSingleCandidate
@ConditionalOnSingleCandidate 是 Spring Boot 提供的一个条件注解(Conditional Annotation) ,用于根据 Spring 容器中是否存在且仅存在一个指定类型的 Bean 候选者(candidate) 来决定是否加载某个配置或 Bean。
19.1 核心作用
只有当 Spring 应用上下文中存在且仅存在一个指定类型的 Bean(即该类型是"单一候选")时,被注解的配置或方法才会生效。
这个注解常用于自动配置(Auto-Configuration) 中,确保在用户已经明确提供了一个 Bean 的情况下,才启用与之配套的辅助组件;如果用户没提供,或者提供了多个(导致歧义),则不启用,避免冲突。
19.2 基本用法
java
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
public MyDatabaseService myDatabaseService(DataSource dataSource) {
return new MyDatabaseService(dataSource);
}
含义:
✅ 只有当容器中有且仅有一个 DataSource 类型的 Bean 时,才创建 MyDatabaseService。
19.3 关键行为说明
| 场景 | 是否满足条件 | 说明 |
|---|---|---|
容器中没有 DataSource Bean |
❌ 不满足 | 因为"不存在" |
容器中有恰好一个 DataSource Bean |
✅ 满足 | 符合"单一候选" |
容器中有多个 DataSource Bean(如主从数据源) |
❌ 不满足 | 存在歧义,不安全 |
容器中有多个 DataSource,但其中一个被 @Primary 标记 |
✅ 满足 | Spring 认为 @Primary 的那个是"主要候选",视为单一 |
✅ 重要规则 :
@ConditionalOnSingleCandidate会考虑 @Primary 注解 。即:如果有多个候选,但存在一个
@Primary的 Bean,则仍视为"单一候选"。
19.4 典型应用场景
✅ 场景 1:自动配置依赖主数据源的服务
Spring Boot 内置的很多自动配置都使用此注解,例如:
java
// Spring Boot 自动配置片段
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnMissingBean // 确保用户没自己定义
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
- 如果用户定义了唯一一个
DataSource→ 自动配JdbcTemplate - 如果用户没定义
DataSource→ 不配(因为前提不满足) - 如果用户定义了两个
DataSource(如读写分离),但标了一个@Primary→ 仍会配JdbcTemplate,并注入@Primary的那个
✅ 场景 2:避免多数据源下的歧义配置
假设你开发一个监控组件,只适用于"单一数据源"场景:
java
@Configuration
public class DataSourceMonitorAutoConfiguration {
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
public DataSourceHealthIndicator healthIndicator(DataSource ds) {
return new DataSourceHealthIndicator(ds);
}
}
在多数据源环境下,用户需自行配置多个
HealthIndicator,而不是由自动配置"猜"该用哪个。
19.5 与类似注解对比
| 注解 | 判断逻辑 |
|---|---|
@ConditionalOnBean(DataSource.class) |
只要存在至少一个 DataSource 就满足 |
@ConditionalOnMissingBean(DataSource.class) |
不存在 DataSource 时满足 |
@ConditionalOnSingleCandidate(DataSource.class) |
存在且唯一(或有 @Primary) 时满足 |
🔍 举例:
- 用户定义了两个
DataSource,无@Primary:
@ConditionalOnBean→ ✅ 生效@ConditionalOnSingleCandidate→ ❌ 不生效
19.6 底层原理
@ConditionalOnSingleCandidate 基于 @Conditional(OnSingleCandidateCondition.class) 实现。
其判断逻辑(简化)如下:
- 调用
beanFactory.getBeanNamesForType(type)获取所有候选 Bean 名称 - 如果数量为 0 → 不满足
- 如果数量为 1 → 满足
- 如果数量 > 1:
- 检查是否有且仅有一个 Bean 被
@Primary标记 - 如果有 → 满足;否则 → 不满足
- 检查是否有且仅有一个 Bean 被
19.7 注意事项
- 仅适用于已注册到容器的 Bean
它检查的是 当前 Spring ApplicationContext 中已定义的 Bean ,不包括尚未处理的@Bean方法。 - 常与 @ConditionalOnMissingBean 配合使用
确保不会覆盖用户自定义的同类型 Bean:
java
@Bean
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnMissingBean(CustomService.class)
public CustomService customService(DataSource ds) { ... }
- 不适用于泛型类型(需谨慎)
对List<String>这类泛型,可能无法精确匹配,建议用具体类型。
19.8 总结
@ConditionalOnSingleCandidate 是 Spring Boot 自动配置体系中的"安全阀",它确保:
- 在明确、无歧义的上下文中启用配套功能
- 避免在多实现(如多数据源、多缓存)场景下做出错误假设
- 尊重用户的
@Primary选择
它是构建健壮、可扩展、用户友好的 Starter 组件的关键工具之一。
二十、@ConfigurationProperties
@ConfigurationProperties 是 Spring Boot 中用于将外部配置(如 application.properties 或 application.yml)绑定到 Java Bean 的核心注解。它提供了一种类型安全、结构清晰的方式来管理应用配置。
20.1 核心作用
自动将配置文件中的属性值映射到一个 Java 对象的字段上,实现"配置即对象"。
相比传统的 @Value("${xxx}") 逐个注入,@ConfigurationProperties 支持:
- 批量绑定
- 类型安全
- 嵌套对象
- 松散绑定(Relaxed Binding)
- 配置校验(JSR-303/380)
- 元数据生成(IDE 自动提示)
20.2 基本用法
1. 定义配置类
java
@Component // 或通过 @EnableConfigurationProperties 注册
@ConfigurationProperties(prefix = "app.user")
public class UserProperties {
private String name;
private int age;
private boolean active;
// getter / setter 必须存在(用于绑定)
}
2. 配置文件(application.yml)
yaml
app:
user:
name: Alice
age: 30
active: true
或 application.properties:
properties
app.user.name=Alice
app.user.age=30
app.user.active=true
3. 使用配置
java
@Service
public class UserService {
private final UserProperties userProps;
public UserService(UserProperties userProps) {
this.userProps = userProps;
}
public void printInfo() {
System.out.println(userProps.getName()); // 输出: Alice
}
}
20.3 关键特性详解
✅ 1. 前缀绑定(prefix)
- 所有以
prefix.开头的属性会自动映射到该类字段。 - 支持多级嵌套:
prefix.sub.field
✅ 2. 松散绑定(Relaxed Binding)
Spring Boot 自动匹配多种命名风格,以下写法等效:
| Java 字段 | properties 写法 | yml 写法 |
|---|---|---|
userName |
app.user.user-name app.user.userName app.user.user_name |
user-name: userName: user_name: |
支持:驼峰、短横线(kebab-case)、下划线、大写等。
✅ 3. 嵌套对象支持
java
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private Database database = new Database();
private Mail mail = new Mail();
public static class Database {
private String url;
private String username;
// getter/setter
}
public static class Mail {
private String host;
private int port;
// getter/setter
}
}
对应 application.yml:
yaml
app:
database:
url: jdbc:mysql://...
username: root
mail:
host: smtp.example.com
port: 587
✅ 4. 集合与 Map 绑定
java
public class AppProperties {
private List<String> servers = new ArrayList<>();
private Map<String, String> metadata = new HashMap<>();
}
yaml
app:
servers:
- server1
- server2
metadata:
env: prod
region: us-east-1
✅ 5. 配置校验(Validation)
添加 @Validated + JSR-303 注解:
java
@Component
@ConfigurationProperties(prefix = "app.user")
@Validated
public class UserProperties {
@NotBlank
private String name;
@Min(1)
@Max(150)
private int age;
// getter/setter
}
启动时若配置不合法,会抛出
BindValidationException。
✅ 6. 默认值支持
通过字段初始化或 @DefaultValue(需配合 @ConstructorBinding):
java
private boolean enabled = true; // 默认为 true
或使用构造函数绑定(推荐不可变配置):
java
@ConstructorBinding
@ConfigurationProperties(prefix = "app.retry")
public class RetryProperties {
private final int maxAttempts;
private final long delayMs;
public RetryProperties(@DefaultValue("3") int maxAttempts,
@DefaultValue("1000") long delayMs) {
this.maxAttempts = maxAttempts;
this.delayMs = delayMs;
}
}
⚠️ 使用
@ConstructorBinding时,类必须是 final,且无 setter。
20.4 注册方式(两种)
方式式 1:直接加 @Component
java
@Component
@ConfigurationProperties(prefix = "app.user")
public class UserProperties { ... }
方式 2:通过 @EnableConfigurationProperties(推荐用于自动配置)
java
@Configuration
@EnableConfigurationProperties(UserProperties.class)
public class AppConfig { }
在 Starter 或 AutoConfiguration 中常用方式 2,避免强制用户扫描该类。
20.5 生成配置元数据(IDE 提示)
添加依赖(Maven):
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
编译后生成 META-INF/spring-configuration-metadata.json,使 IDE(IntelliJ/Eclipse)能:
- 自动提示配置项
- 标记未知属性(警告)
- 显示默认值和描述
可配合 @Description 使用:
java
public class UserProperties {
@Description("用户姓名,不能为空")
private String name;
}
20.6 与 @Value 对比
| 特性 | @ConfigurationProperties |
@Value |
|---|---|---|
| 批量绑定 | ✅ 支持整个对象 | ❌ 只能单个属性 |
| 类型安全 | ✅ 编译期检查 | ❌ 字符串模板,易出错 |
| 松散绑定 | ✅ 自动适配命名风格 | ❌ 必须完全匹配 |
| 嵌套/集合 | ✅ 原生支持 | ❌ 需 SpEL,复杂 |
| 校验 | ✅ 支持 JSR-303 | ❌ 不支持 |
| IDE 提示 | ✅ 生成元数据 | ❌ 无 |
| 使用场景 | 复杂配置、模块化配置 | 简单单个值 |
📌 官方建议:
"Whenever you have multiple properties of the same type, strongly consider using
@ConfigurationPropertiesinstead of@Value."
20.7 常见问题
❓ 为什么 setter 必须存在?
- Spring Boot 通过 JavaBean 规范(getter/setter)进行反射赋值。
- 若使用
@ConstructorBinding,则通过构造函数注入,无需 setter。
❓ 配置没生效?
- 检查是否注册了 Bean(加了
@Component或@EnableConfigurationProperties) - 检查
prefix是否正确 - 检查字段是否有 public setter
20.8 总结
@ConfigurationProperties 是 Spring Boot 配置管理的最佳实践,它让配置:
- 结构化(对象代替字符串)
- 类型安全(编译检查)
- 可维护(集中管理)
- 可验证(启动失败早发现)
- 开发者友好(IDE 智能提示)
适用于所有中大型项目,是构建云原生、12-Factor 应用的关键工具之一。