在 Spring Boot 项目中读取配置,最直接的方式是使用 @Value 注解逐个注入属性。配置项少的时候这样做没什么问题,但一旦配置结构复杂起来,比如存在多级嵌套、列表、过期时间这类需要统一单位的值,@Value 的短板就暴露了:字符串写错不会有编译期提示,缺失配置只能等运行时抛异常才发现,而且分散在各个类中的配置注入让维护变得困难。@ConfigurationProperties 提供了另一种思路——把一组相关配置绑定到一个 POJO 上,由框架负责类型转换和注入,整个过程是类型安全的。

一、基本用法:用一个 POJO 承载配置
假设应用的配置文件里有如下内容:
app:
name: order-service
timeout: 30s
max-connections: 100
endpoints:
- /api/order
- /api/pay
针对这段配置,可以定义一个对应的属性类。注意类上标注 @ConfigurationProperties,prefix 属性指定配置的前缀,字段名与配置键一一对应:
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String name;
private Duration timeout; // 自动支持 30s、500ms 等写法
private int maxConnections;
private List<String> endpoints = new ArrayList<>();
// 省略 getter 和 setter,绑定依赖 setter 注入,
// 没有 setter 的话对应字段不会被赋值
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Duration getTimeout() { return timeout; }
public void setTimeout(Duration timeout) { this.timeout = timeout; }
public int getMaxConnections() { return maxConnections; }
public void setMaxConnections(int maxConnections) { this.maxConnections = maxConnections; }
public List<String> getEndpoints() { return endpoints; }
public void setEndpoints(List<String> endpoints) { this.endpoints = endpoints; }
}
这里有一个细节值得注意:Duration 类型的字段可以直接接收 30s、500ms、1h 这样的值,Spring 会自动完成转换。如果不写单位,也可以在字段上使用 @DurationUnit(ChronoUnit.SECONDS) 指定默认单位,避免不同环境配置写法不一致带来的隐患。类似的还有 DataSize,支持 10MB、512KB 等内存容量写法。
绑定过程依赖 setter 方法,这一点和构造器注入不同。字段上直接赋的初始值会被保留为默认值——只有配置文件中存在对应键时才会覆盖它。这个特性常用来给配置项设置合理的兜底值,比如上面的 endpoints 初始化为空列表,即使配置缺失也不会出现空指针。
二、两种注册方式及对比
属性类写好后还需要交给 Spring 容器管理,常见有两种做法。第一种是直接在类上加 @Component:
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
// ...
}
第二种是不加 @Component,改在配置类或启动类上通过 @EnableConfigurationProperties 启用:
@SpringBootApplication
@EnableConfigurationProperties(AppProperties.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
两种方式效果等价,但各有适用场景。@Component 简单直接,适合自己项目内的属性类;@EnableConfigurationProperties 则是编写 starter 或公共组件时的标准做法,因为它不会强迫使用者扫描你的包路径,只需一个注解即可完成装配。如果属性类较多,还可以用 @ConfigurationPropertiesScan 一次性扫描注册,免去逐个列出的麻烦。
注册之后,在其他 Bean 中就能以类型安全的方式使用配置,注入的是完整的 AppProperties 对象,IDE 可以自动补全字段,重构字段名时编译器也会同步检查所有引用点:
@Service
public class OrderService {
private final AppProperties properties;
public OrderService(AppProperties properties) {
this.properties = properties;
}
public void connect() {
Duration timeout = properties.getTimeout();
int pool = properties.getMaxConnections();
// ...
}
}
三、宽松绑定规则与嵌套结构
@ConfigurationProperties 采用了宽松绑定策略,配置键的写法相当灵活。字段名为 maxConnections 时,配置文件里写 max-connections、maxConnections、MAX_CONNECTIONS 甚至环境变量风格的 APP_MAXCONNECTIONS,都能正确绑定。这种设计是为了适配不同配置源的命名习惯——properties 文件、YAML、环境变量各有各的约束,宽松绑定屏蔽了这些差异。不过要注意,@Value 不支持宽松绑定,它要求占位符与配置键严格一致,这也是两者一个容易被忽视的区别。
嵌套配置只需要定义嵌套的内部类,绑定会递归进行:
app:
security:
enabled: true
token-header: X-Auth-Token
roles:
admin: read,write
guest: read
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private final Security security = new Security();
public Security getSecurity() { return security; }
public static class Security {
private boolean enabled;
private String tokenHeader;
private Map<String, List<String>> roles = new HashMap<>();
// getter / setter 省略
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getTokenHeader() { return tokenHeader; }
public void setTokenHeader(String tokenHeader) { this.tokenHeader = tokenHeader; }
public Map<String, List<String>> getRoles() { return roles; }
public void setRoles(Map<String, List<String>> roles) { this.roles = roles; }
}
}
上面例子还演示了 Map<String, List<String>> 这种复合类型的绑定:admin 作为 key,read,write 会被自动拆分为字符串列表。对于集合类字段,推荐直接初始化为不可变空对象或空集合,这样即使配置文件里没有对应段落,业务代码拿到的也是一个合法对象,不必到处做判空。
四、配置校验与默认值处理
仅靠绑定还不够,配置值本身的合法性同样重要。引入 spring-boot-starter-validation 依赖后,在类上加 @Validated,就可以对字段使用 JSR-303 校验注解:
@Validated
@ConfigurationProperties(prefix = "app")
public class AppProperties {
@NotBlank(message = "app.name 不能为空")
private String name;
@NotNull
@DurationMin(value = 1, unit = ChronoUnit.SECONDS)
@DurationMax(value = 60, unit = ChronoUnit.SECONDS)
private Duration timeout;
@Min(1)
@Max(1000)
private int maxConnections;
// getter / setter 省略
}
校验失败时应用启动阶段就会直接报错并打印具体哪个键违反了哪条规则,把问题拦截在部署之前,而不是等到线上某个请求超时才排查。对于嵌套对象,需要在外层字段上加 @Valid 才能让校验递归到内部类,这是实际使用中经常被遗漏的一点。
另一个实用技巧是利用构造器绑定实现真正的不可变配置类。把类声明为只有构造器的形式(不提供 setter),并用 @DefaultValue 指定默认值,Spring 会通过构造器完成绑定,对象天然线程安全:
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private final String name;
private final Duration timeout;
private final int maxConnections;
public AppProperties(
@DefaultValue("default-app") String name,
@DefaultValue("30s") Duration timeout,
@DefaultValue("100") int maxConnections) {
this.name = name;
this.timeout = timeout;
this.maxConnections = maxConnections;
}
}
最后如果希望 IDE 在编辑配置文件时给出自动补全提示,可以添加 spring-boot-configuration-processor 依赖,编译时会生成配置元数据文件,application.yml 中输入前缀即可看到所有支持的字段及其说明。总体来说,@ConfigurationProperties 把配置从散落的字符串聚合为结构化的类型对象,配合校验、默认值和元数据,是 Spring Boot 项目中管理配置的推荐方案,复杂度越高的项目收益越明显。
ConfigurationPropertiesSpring Boot配置绑定类型安全配置修改时间:2026-09-13 17:12:56