配置属性绑定
本教程共 48 篇 · 第 11 篇 · 更新于 2026-08-13 · 约 4 分钟阅读
本节目标:用 @ConfigurationProperties 把配置绑成类型安全对象,掌握注册、嵌套、校验和与 @Value 的取舍。
为什么需要类型安全绑定
上一章里,我们用 @Value 读单个属性。属性一多,问题就来了:
- 十几个 @Value 散落在类里,乱。
- 值全是字符串,类型转换靠自己。
- 拼错键名,运行到一半才报错。
@ConfigurationProperties 换个思路:把一组前缀相同的属性,整体绑进一个 Java 对象。
类型转换、键名校验,框架全包了。这就是「类型安全配置」。
第一个绑定示例
先写配置,放在 src/main/resources/application.yml:
app:
name: hello-app
version: 1.0.0
author: 码上学
再写一个普通 Java 类接收它:
import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private String version;
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
三个要点:
@ConfigurationProperties(prefix = "app")声明前缀。@Component让它成为 Bean。- JavaBean 规范要求提供 getter/setter,绑定靠 setter 完成。
Noteprefix 必须用小写 kebab-case,比如
my-app。写myApp或MyApp会出问题。
其他类直接注入这个对象即可:
import org.springframework.stereotype.Service;
@Service
public class AppService {
private final AppProperties props;
public AppService(AppProperties props) {
this.props = props;
}
public String describe() {
return props.getName() + " v" + props.getVersion() + " by " + props.getAuthor();
}
}
三种注册方式
@Component 只是其中一种。官方还提供两种:
方式一:@EnableConfigurationProperties
适合不想给类加 @Component 的场景:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(AppProperties.class)
public class AppConfig {
}
方式二:@ConfigurationPropertiesScan
在主类上开启扫描,自动发现所有 @ConfigurationProperties 类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三种方式任选其一。小型项目用 @Component 最省事,组件库场景常用后两种。
嵌套对象
配置有层级时,用嵌套类对应:
app:
name: hello-app
security:
username: admin
password: secret
roles:
- USER
- ADMIN
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private Security security = new Security();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Security getSecurity() {
return security;
}
public void setSecurity(Security security) {
this.security = security;
}
public static class Security {
private String username;
private String password;
private List<String> roles = new ArrayList<>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
}
}
嵌套对象预初始化后可以省略 setter,绑定器直接往里填值。
绑定 Map
键不固定的配置,用 Map 接。比如一组按节点名区分的地址:
app:
nodes:
node-1:
ip: 10.0.0.1
port: 8080
node-2:
ip: 10.0.0.2
port: 8080
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private Map<String, Node> nodes = new LinkedHashMap<>();
public Map<String, Node> getNodes() {
return nodes;
}
public void setNodes(Map<String, Node> nodes) {
this.nodes = nodes;
}
public static class Node {
private String ip;
private int port;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
}
Map 的 key 就是配置里的键名,运行时动态取:
appProperties.getNodes().get("node-1").getIp();
绑定第三方组件
类不是自己写的,没法加注解。
这时把 @ConfigurationProperties 放到 @Bean 方法上:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ThirdPartyConfig {
@Bean
@ConfigurationProperties(prefix = "another")
public AnotherComponent anotherComponent() {
return new AnotherComponent();
}
}
another 前缀下的属性会自动绑到 AnotherComponent 实例上。
前提是第三方类遵循 JavaBean 规范,有对应的 setter。
构造器绑定与 record
想要不可变对象?用构造器绑定。类上只有一个有参构造器时,Spring Boot 自动启用:
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private final String name;
private final Security security;
public AppProperties(String name, Security security) {
this.name = name;
this.security = security;
}
public String getName() {
return name;
}
public Security getSecurity() {
return security;
}
public static class Security {
private final String username;
private final String password;
private final List<String> roles;
public Security(String username, String password,
@DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public List<String> getRoles() {
return roles;
}
}
}
@DefaultValue 给缺失的属性提供默认值。字段全变 final,对象不可变,更安全。
Note构造器绑定的类不能靠
@Component注册(官方明确不支持),必须用@EnableConfigurationProperties(AppProperties.class)或在启动类加@ConfigurationPropertiesScan启用扫描。
Java 16+ 还可以直接用 record:
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "app")
public record AppProperties(String name, String version, String author) {
}
record 同样不能靠 @Component 注册,用 @EnableConfigurationProperties(AppProperties.class) 或 @ConfigurationPropertiesScan 启用。
Note构造器绑定要求编译时带
-parameters参数。用 spring-boot-starter-parent 或官方 Gradle 插件时自动开启,不用管。
宽松绑定
Spring Boot 的绑定规则很宽松。字段 firstName 可以接受这些写法:
| 写法 | 示例 |
|---|---|
| kebab-case(推荐) | app.first-name |
| 驼峰 | app.firstName |
| 下划线 | app.first_name |
| 环境变量 | APP_FIRSTNAME |
官方建议配置文件里统一用小写 kebab-case。
好处是:换环境、换配置来源时,键名不用改。这一特性在第 12 章的环境变量部分还会用到。
属性校验
配合 Jakarta Validation,启动时就能校验配置。先加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
再给配置类加上校验注解:
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
@Component
@ConfigurationProperties(prefix = "app")
@Validated
public class AppProperties {
@NotEmpty
private String name;
@NotNull
@Size(min = 1, max = 10)
private String version;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
校验失败时应用直接启动失败,并给出明确错误信息。
嵌套对象的校验要加 @Valid 才会级联生效:
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
@Valid
private Security security = new Security();
public static class Security {
@NotEmpty
private String username;
}
Tip配置写错了早失败,比运行时炸掉好得多。关键配置务必加校验。
类型转换
字符串属性会自动转成目标类型。
int、long、boolean、枚举、日期都没问题。官方还内置了 Duration 和 DataSize 的转换:
app:
timeout: 30s
max-size: 10MB
import java.time.Duration;
import org.springframework.util.unit.DataSize;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private Duration timeout;
private DataSize maxSize;
public Duration getTimeout() {
return timeout;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
public DataSize getMaxSize() {
return maxSize;
}
public void setMaxSize(DataSize maxSize) {
this.maxSize = maxSize;
}
}
30s 自动解析为 30 秒,10MB 解析为 10 兆字节。
这类可读写法比裸数字友好得多,配置里优先用。
IDE 自动补全
@ConfigurationProperties 类还能给 IDE 提供元数据,写配置时有自动补全和文档提示。
加一个可选依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
编译时它会扫描配置类,生成 spring-configuration-metadata.json。
重新编译后,在 application.yml 里输入 app. 就有候选提示。
Tip想给属性写注释说明?在字段上加 Javadoc 注释,IDE 提示会显示出来。
@ConfigurationProperties 与 @Value 怎么选
官方对比表浓缩成三行:
| 能力 | @ConfigurationProperties | @Value |
|---|---|---|
| 宽松绑定 | 完整支持 | 有限支持 |
| 元数据与 IDE 提示 | 支持 | 不支持 |
| SpEL 表达式 | 不支持 | 支持 |
选型建议:
- 一组相关属性 → 用 @ConfigurationProperties。
- 单个零散属性 → 用 @Value。
- 需要 SpEL 计算(如
${random.int}之外的表达式)→ 只能用 @Value。
记住一句话:自己定义的配置组,优先 @ConfigurationProperties。
小结
- @ConfigurationProperties 把同前缀属性绑成对象,类型安全。
- 注册方式有 @Component、@EnableConfigurationProperties、@ConfigurationPropertiesScan 三种。
- 支持嵌套、列表、Map、构造器绑定和 record。
- 加 @Validated 后可用 Jakarta Validation 校验配置。
- 一组属性用 @ConfigurationProperties,单个属性用 @Value。
- 第三方类用 @Bean 方法绑定。
- Map、Duration、DataSize 都有内置支持。
- 加 configuration-processor 依赖,IDE 补全配置键。