学完这篇你能得到一套能直接落地的 Java 配置管理方案:知道什么场景用 Properties、什么场景用 YAML、什么时候该上配置中心,以及每一步具体怎么写代码、怎么避开常见坑。
第一步:先分清三种方案的适用场景
- Properties:键值对、ISO-8859-1 编码(这点很关键)、无层级。适合极简工具类项目、老系统、需要被非 Java 程序读取的配置。
- YAML:有层级、可读性好、支持列表和复杂对象。适合 Spring Boot 项目的主配置。
- 配置中心:配置存在远端(Nacos / Apollo / Consul),支持热更新、多环境隔离、灰度、权限审计。适合多实例部署、需要不停机改配置的场景。
一句话原则:单机单实例用文件,多实例要热更新就上配置中心。
第二步:Properties 的正确读写方式
用 java.util.Properties 读取,注意必须用 InputStream 而不是 Reader,否则中文会乱码:
Properties p = new Properties();
try (InputStream in = Files.newInputStream(Paths.get("app.properties"))) {
p.load(in); // 用 InputStream,按 ISO-8859-1 解码
}
String name = p.getProperty("app.name", "default");
如果要写中文,两个办法:一是 native2ascii 转义(老办法),二是 JDK 9+ 用 p.load(new InputStreamReader(in, StandardCharsets.UTF_8)) 显式指定编码。
注意:Properties.load(Reader) 在 JDK 8 里是按字符读的,但老项目里常见的 new FileInputStream + 默认编码组合,在 Windows(GBK)和 Linux(UTF-8)之间迁移时一定会出乱码,配置文件统一存 UTF-8 并显式指定编码最稳。
第三步:用 SnakeYAML 读 YAML
Maven 依赖:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.2</version>
</dependency>
读取方式:
Yaml yaml = new Yaml();
try (InputStream in = Files.newInputStream(Paths.get("app.yaml"))) {
Map<String, Object> map = yaml.load(in);
}
层级结构会变成嵌套的 Map,取值要走 ((Map)map.get("server")).get("port"),很啰嗦。所以实际项目里几乎都直接用 Spring Boot 的绑定能力,见下一步。
注意:YAML 不允许 Tab 缩进,只能用空格;冒号后面必须有空格;snakeyaml 2.x 默认用 SafeConstructor 语义,Yaml.load 反序列化任意类已被收紧,别再用老教程里的 yaml.loadAs(in, SomeClass.class) 传不可信内容。
第四步:Spring Boot 里用 @ConfigurationProperties 绑定
比 @Value 好用的地方在于:支持层级、支持列表、支持类型转换、支持 JSR-303 校验、支持松散绑定(relaxed binding)。
@Component
@ConfigurationProperties(prefix = "app.mail")
@Validated
public class MailProperties {
@NotBlank private String host;
private int port = 25;
private List<String> whitelist = new ArrayList<>();
// getter / setter
}
对应 YAML:
app:
mail:
host: smtp.example.com
port: 465
whitelist:
- a@example.com
- b@example.com
松散绑定意味着 app.mail.host、app.mail.HOST、环境变量 APP_MAIL_HOST 都能绑上,这点在容器化部署时非常有用。
注意:@ConfigurationProperties 的类必须有 setter(或使用构造器绑定 + @ConstructorBinding);字段是 List/Map 时不要用 final 且别忘初始化,否则绑定会静默失败。
第五步:多环境与覆盖优先级
Spring Boot 的配置优先级从高到低大致是:命令行参数 > 环境变量 > application-{profile}.yml > application.yml。
用 profile 拆环境:
spring:
profiles:
active: dev
启动时覆盖:java -jar app.jar --spring.profiles.active=prod --app.mail.port=587
容器里用环境变量:APP_MAIL_PORT=587(下划线对应点号,全大写)。
注意:不要把生产密码写进 application-prod.yml 提交到 Git。真要做版本管理,把敏感项抽成 ${DB_PASSWORD} 占位符,值从环境变量注入,缺值时启动直接报错,比默认值安全。
第六步:敏感配置加密
本地文件方案可以用 Jasypt:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
配置项写成 ENC(密文),启动时用 -Djasypt.encryptor.password=xxx 传入主密钥。
注意:主密钥绝不能和密文放在同一个仓库或同一个配置文件里,否则加密等于没做。
第七步:接配置中心(以 Nacos 为例)
- 引入依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
bootstrap.yml 里写服务端地址与 dataId:
spring:
application:
name: order-service
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
- 需要热更新的 Bean 加
@RefreshScope,配合 @Value 或 @ConfigurationProperties 使用,改完配置在 Nacos 控制台发布即可生效,无需重启。
注意:@RefreshScope 的 Bean 在刷新时会重建代理对象,如果你在里面缓存了状态(比如连接池、本地缓存),刷新后这些状态会丢,关键资源要放在不会被刷新的 Bean 里。另外 Spring Cloud 2020 之后默认不再加载 bootstrap.yml,需要额外引入 spring-cloud-starter-bootstrap。
小结
- 单机项目:Properties 或 YAML 都行,YAML 更适合有层级和列表的配置。
- 编码问题是 Properties 最大的坑,统一 UTF-8 并显式指定,别依赖平台默认编码。
- Spring Boot 里优先用
@ConfigurationProperties,别满屏 @Value。
- 多环境靠 profile,敏感信息靠环境变量或加密,别硬编码进仓库。
- 多实例、要热更新、要审计,就上配置中心;上了之后注意
@RefreshScope 的作用范围和 bootstrap 加载问题。