做后端的人大概都跟配置打过交道。Java 这边 YAML 用得最多,Spring Boot 里到处都是 application.yml。但很多时候我们要的不只是"加载配置",而是把 YAML 当模板来加工:环境不同配置不同、模板里有变量、日志里不能带密码。

这篇文章聊聊我最近处理的几个场景,以及最后怎么用 sapera-yaml 解决的。库是开源的,坐标:

<dependency>
    <groupId>cn.frostlock</groupId>
    <artifactId>sapera-yaml</artifactId>
    <version>0.1.0</version>
</dependency>

场景一:环境配置合并

开发、测试、生产三套配置,公共部分抽成 base,环境相关的各自一份,启动时合并。以前用 SnakeYAML 手写合并,list 的处理最别扭——到底覆盖还是拼接,每次都要自己定。

YamlDocument base = SaperaYaml.load(baseYaml);
YamlDocument env = SaperaYaml.load(envYaml);
String merged = base.merged(env, MergeStrategy.REPLACE_LISTS).toYaml();

map 递归合并,标量后者覆盖前者。list 默认整体替换,想拼接就换 CONCAT_LISTS,一个枚举搞定。

场景二:把嵌套配置拍平

有个内部工具只认 key-value,得把嵌套的 YAML 拍平传过去:

Map<String, Object> flat = SaperaYaml.flatten(yamlText);
// server.port -> 8080
// server.hosts[0] -> "a"

拍平出来的 key 本身就是路径语法,用的时候可以直接 get,想还原就 unflatten 回去,往返无损。

场景三:模板占位符替换

配置模板里写 ${port} 这种占位符,加载时用环境变量填:

YamlDocument resolved = doc.interpolate(key -> {
    String v = System.getenv(key);
    return v != null ? v : System.getProperty(key);
});

占位符解析不到会直接抛异常,不会悄悄留个 ${xxx} 在配置里带病运行——这点我觉得比很多模板库实在。

场景四:日志脱敏

打配置日志前把敏感字段打码,排查线上问题的时候很有用:

YamlDocument safe = doc.mask("***", "server.password", "db.password");

几个提醒

SnakeYAML 默认对别名和输入大小有保护(别名上限 50、码点 3MB),sapera-yaml 沿用这些默认值,别指望拿它解析无限大的文件。

重复 key 默认是"后者覆盖前者"。如果你希望配置里出现重复 key 直接报错,用 loadStrict:

YamlDocument doc = YamlDocument.loadStrict("a: 1\na: 2\n"); // 抛异常

最后说一句:这类"配置加工"的事,其实谁都能用 SnakeYAML 手搓,但每次都要重写一遍 merge、flatten、脱敏,时间就这么没了。封装一次,后面就省心了。