这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 配置管理决定应用在不同环境中的行为。Spring Environment 把配置源抽象成 PropertySource,并通过统一的解析、优先级和类型转换机制提供给 Bean。
6.1 配置来源
常见来源:
命令行参数
SPRING_APPLICATION_JSON
环境变量
系统属性
application-{profile}.yml
application.yml
@PropertySource
默认值
不同版本和配置中心的优先级可能不同。确认优先级的可靠方式是输出 Environment 的 PropertySources。
6.2 Environment 抽象
@Component
public class ConfigPrinter {
private final ConfigurableEnvironment environment;
public ConfigPrinter(ConfigurableEnvironment environment) {
this.environment = environment;
}
public void print() {
environment.getPropertySources().forEach(ps ->
System.out.println(ps.getName() + " = " + ps.getSource()));
}
}
核心接口:
| 接口 | 作用 |
|---|---|
PropertyResolver |
读取和解析属性 |
Environment |
环境属性与 Profile |
ConfigurableEnvironment |
可修改 PropertySource |
PropertySource |
单个配置来源 |
MutablePropertySources |
有序配置源集合 |
6.3 application.yml
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://localhost:3306/order?useSSL=false
username: order
password: ${DB_PASSWORD}
jpa:
open-in-view: false
pay:
url: https://pay.example.com
timeout: 3s
retry-times: 2
推荐:
- 使用
kebab-case; - 有单位的时间用
Duration; - 敏感信息来自环境变量或密钥系统;
- 每个环境只写差异配置;
- 配置键集中到 Properties 类。
6.4 Profile
spring:
profiles:
active: dev
---
spring:
config:
activate:
on-profile: dev
pay:
url: http://localhost:8081
---
spring:
config:
activate:
on-profile: prod
pay:
url: https://pay.example.com
激活方式:
java -jar app.jar --spring.profiles.active=prod
SPRING_PROFILES_ACTIVE=prod java -jar app.jar
不要用 Profile 表达业务开关,例如 feature-pay-v2。Profile 适合环境差异,业务开关应有自己的配置键和灰度体系。
6.5 配置绑定
推荐 @ConfigurationProperties:
@ConfigurationProperties(prefix = "pay")
public record PayProperties(
URI url,
Duration timeout,
int retryTimes,
List<String> fallbackRegions) {
}
启用:
@ConfigurationPropertiesScan
@SpringBootApplication
public class Application {
}
校验:
@Validated
@ConfigurationProperties(prefix = "pay")
public record PayProperties(
@NotNull URI url,
@NotNull Duration timeout,
@Min(0) @Max(5) int retryTimes) {
}
优点:
- 配置集中;
- 类型安全;
- IDE 提示;
- 启动时校验;
- 易写单元测试。
6.6 松绑定
以下写法都可以绑定到 pay.retryTimes:
pay.retry-times=3
pay.retryTimes=3
PAY_RETRY_TIMES=3
环境变量通常使用大写下划线:
SPRING_DATASOURCE_URL
PAY_RETRY_TIMES
注意:
- 不要混用多种风格;
- Map 和 List 的绑定规则更细;
- 缩短环境变量名需要显式配置;
- 环境变量值都是字符串,绑定到集合或对象时要看清规则。
6.7 占位符与默认值
pay.url=${PAY_URL:https://default.example.com}
app.name=${spring.application.name}
app.description=${app.name} order backend
支持 SpEL 的场景要谨慎使用:
app.token-ttl=#{60 * 60}
过度使用表达式会让配置难以追踪。简单占位符优先。
6.8 配置中心
本地配置之外,微服务常使用:
| 方案 | 特点 |
|---|---|
| Spring Cloud Config | Git 后端,配置版本化 |
| Nacos | 注册与配置一体 |
| Consul | 多云生态常用 |
| Kubernetes ConfigMap | 平台原生 |
| Vault | 密钥管理 |
优先级建议:
本地代码:默认值和非敏感配置
环境变量:简单覆盖
配置中心:环境差异和动态开关
密钥系统:密码、证书、Token
不要把数据库密码提交到 Git,也不要把所有业务配置都堆进环境变量。
6.9 动态配置
配置中心刷新示例:
@RefreshScope
@Component
public class RiskClient {
private final String endpoint;
public RiskClient(@Value("${risk.endpoint}") String endpoint) {
this.endpoint = endpoint;
}
}
刷新后 @RefreshScope Bean 会被重建。要注意:
- 旧 Bean 中持有的状态会丢失;
- 依赖它的对象引用可能仍指向旧代理目标;
- 动态开关应原子读取;
- 重要配置变更要有审计和灰度;
- 连接类配置重建成本高。
更简单的动态开关可以自己维护内存快照:
@Component
public class FeatureFlags {
private final AtomicReference<Map<String, Boolean>> flags =
new AtomicReference<>(Map.of());
public void update(Map<String, Boolean> next) {
flags.set(Map.copyOf(next));
}
public boolean enabled(String key) {
return flags.get().getOrDefault(key, false);
}
}
6.10 配置安全
常见风险:
- 明文密码提交仓库;
- Actuator 暴露 env;
- 日志打印配置对象;
- 配置中心权限过宽;
- 配置变更没有审计;
- 测试配置误用于生产;
- 默认账号密码未修改。
建议:
- 使用 Vault、KMS 或云 Secret;
- 日志脱敏;
- 限制 Actuator 端点;
- 配置中心按命名空间授权;
- 保存配置变更历史;
- 启动时校验生产必需键;
- 本地开发配置与生产密钥隔离。
6.11 排查配置问题
@Component
public class EnvironmentDiagnostics {
public void print(String key) {
Environment env;
}
}
更直接的做法:
public void print(ConfigurableEnvironment env) {
System.out.println(env.getProperty("pay.timeout"));
env.getPropertySources().stream()
.filter(ps -> ps.containsProperty("pay.timeout"))
.forEach(ps -> System.out.println(ps.getName()));
}
Actuator 端点:
/actuator/env
/actuator/configprops
生产上应关闭或严格保护这些端点。
常见问题:
| 问题 | 原因 |
|---|---|
| 配置未生效 | 优先级低、缩进错误、Profile 未激活 |
| 类型绑定失败 | 格式错误、单位缺失 |
| 环境变量没绑定 | 命名不符合松绑定规则 |
| 密码泄露 | env 端点或日志输出 |
| 刷新无效 | Bean 不在刷新范围或引用未更新 |
本章小结
Spring Environment 把命令行、系统属性、环境变量、Profile 文件和配置中心统一成有序 PropertySource。业务配置应通过 @ConfigurationProperties 集中绑定和校验,敏感信息交给密钥系统。多环境使用 Profile,业务开关使用显式配置和动态刷新机制,并用安全手段控制配置可见性。
思考题
- PropertySource 的顺序为什么重要?
@Value和@ConfigurationProperties各适合什么场景?- 环境变量如何映射到嵌套配置?
@RefreshScope有哪些副作用?- 如何保证生产配置缺失时应用无法启动?