YAML 和 Properties 有什么区别?怎么选
YAML 和 Properties 都是用来写配置的文本格式,区别在于:Properties 是扁平的 key=value 键值对,YAML 用缩进表达层级结构。配置层级深、带列表、要分环境时选 YAML 更省事;只有几个简单键值、或者必须被 Java 的 Properties/ResourceBundle 直接读取(比如 i18n 文案)时,用 Properties 更稳。Spring Boot 两种都支持,同时存在时 properties 优先于 yml。
一、同一份配置,两种写法
先看一个典型的 Spring Boot 配置:端口、数据源、一个跨域白名单列表、一个开关。下面是 Properties 写法:
server.port=8081
server.servlet.context-path=/api
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false
spring.datasource.username=root
spring.datasource.password=Abc@123
app.cors.allowed-origins[0]=https://a.example.com
app.cors.allowed-origins[1]=https://b.example.com
app.feature.enabled=true
等价的 YAML 写法:
server:
port: 8081
servlet:
context-path: /api
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?useSSL=false
username: root
password: Abc@123
app:
cors:
allowed-origins:
- https://a.example.com
- https://b.example.com
feature:
enabled: true
这两份文件我用 Spring Boot 3.5.7 自带的加载器分别解析过,展开后的 8 个属性(键和值)完全一致。可以看出 Properties 的每一行都重复写着 spring.datasource.、app.cors. 这样的前缀,而 YAML 把公共前缀提成了层级,只写一次。条目越多,这个差别越明显,YAML 读起来也更像一棵树。
如果你手上已经有一份 properties 想看看对应的 YAML,不用手敲,直接用本站的 Properties ⇄ YAML 在线转换器,粘贴后点 to yaml 就行;反过来点 to prop 也可以。
二、语法差异对照表
下面这张表把两种格式在日常写配置时最常碰到的差异放在一起。涉及 Spring Boot 行为的条目,我都在 Spring Boot 3.5.7(内置 SnakeYAML 2.4)上实际跑过。
| 对比项 | Properties | YAML |
|---|---|---|
| 结构 | 扁平,靠点号 a.b.c 约定层级 | 缩进表示层级,原生支持嵌套 |
| 注释 | 以 # 或 ! 开头的整行 | 只有 #,可写在行尾(# 前要有空格) |
| 键值分隔 | =、: 或空白都行 | 必须是 : 且后面要跟空格 |
| 列表 | Spring Boot 约定 a[0]=x,或逗号分隔 a=x,y | - x,或 [x, y] |
| 多行值 | 行尾加反斜杠续行 | | 保留换行,> 折叠换行 |
| 数据类型 | 全是字符串,由读取方负责转换 | 解析器会按规则推断布尔、数字、空值(有坑,见下文) |
| 缩进敏感 | 否 | 是,且禁止用 Tab |
| 一个文件多套配置 | Spring Boot 2.4+ 支持用 #--- 分隔 | 用 --- 分隔文档 |
| 重复键 | 后面的覆盖前面的 | Spring Boot 直接报 found duplicate key;有的解析器静默取最后一个 |
| 复用片段 | 不支持 | 支持锚点 &、引用 * 和合并键 << |
| 中文 | Spring Boot 按 ISO-8859-1 读取,直接写 UTF-8 中文会乱码 | 按 UTF-8 读取,直接写中文即可 |
表里"重复键"一行是我实测的:把 app: 在同一层写两遍,Spring Boot 启动时抛出 DuplicateKeyException,信息是 found duplicate key app;而 PyYAML 会静默保留最后一个,前面写的整段都丢了,没有任何提示。所以 YAML 并不总是"更宽容",只是出错的方式不同。
三、什么时候选 YAML
- 层级深、条目多。微服务的
application.yml动辄上百行,公共前缀只写一次,改某个子树时也不容易漏行。 - 配置里有列表或对象列表。比如多个下游服务的地址、多组数据源。用 properties 写成
servers[0].name、servers[0].port、servers[1].name很容易下标写错,YAML 的- name: a一眼就能看出分组。 - 需要分环境。一个 yml 里用
---分出 dev、test、prod 几段,对比差异比翻三个文件方便(具体写法见 迁移指南)。 - 想在配置里写说明。两种格式都能写注释,但缩进结构让注释和它所属的配置块对应得更清楚。
- 需要复用重复片段。多个服务共享同一组超时、重试参数时,可以用锚点和合并键。我在 Spring Boot 3.5.7 上试过,
<<: *defaults会被正确展开,后写的同名键能覆盖默认值。
四、什么时候选 Properties
- 只有零星几个键值。三五行配置没必要引入缩进规则,也就没有缩进写错的风险。
- 国际化文案。
messages.properties这类资源文件走的是 Java 的ResourceBundle机制,属于 properties 的主场,换成 YAML 反而不通用。 - 要被脚本或运维直接处理。
grep、sed、cut -d=对一行一个键值的文件非常友好,而 YAML 的一个值可能分散在多行里。 - 用
@PropertySource加载额外文件。这个注解默认只会按 properties 格式解析,传一个 yml 进去不会报错,但读出来的键是错的(实测结果见下文)。 - 团队里有人经常手改配置、又没有 YAML 校验。少一个空格或者误用 Tab,YAML 就起不来;properties 几乎不会因为格式问题整体失败。
五、三个最容易踩的坑
1. 两种文件同时存在,谁生效?
在 Spring Boot 里,同一个目录下如果既有 application.properties 又有 application.yml,properties 的优先级更高。两个文件里没冲突的键会合并,冲突的键以 properties 为准。我用下面这一对文件实测:
app.name=from-properties
app:
name: from-yml
note: only-in-yml
启动后读取,app.name 是 from-properties,而 app.note 是 only-in-yml。这就是很多人"改了 yml 死活不生效"的原因:旧的 properties 文件还躺在项目里。如果两个文件里都配置了同一个列表,结果也不是合并,而是优先级高的那个整体替换另一个——我用 yml 里写三个元素、properties 里写 app.hosts[0]=x 试过,最终列表只有 [x]。
2. YAML 会"聪明"地改变值的类型
Spring Boot 使用的 SnakeYAML 遵循 YAML 1.1 的类型规则,下面这些写法在 Spring Boot 3.5.7 里的实测结果可能出乎意料:
| 你写的 | 读到的值 | 类型 |
|---|---|---|
| enabled: yes | true | Boolean |
| mode: off | false | Boolean |
| country: NO | false | Boolean |
| zip: 001234 | 668 | Integer(按八进制解析) |
| time: 12:30:45 | 45045 | Integer(六十进制) |
| version: 3.10 | 3.1 | Double |
处理办法很简单:只要值"长得像"布尔、数字、时间,但你想要的是字符串,就加引号,比如 country: "NO"、zip: "001234"。完整的类型陷阱清单和不同解析器之间的差异,放在 YAML 常见语法错误排查 里。
3. 中文:properties 的历史包袱
Java 的 Properties.load(InputStream) 按 ISO-8859-1 读取字节,Spring Boot 读 application.properties 时同样如此。我把 app.name=中文名称 以 UTF-8 保存后启动 Spring Boot 3.5.7,读出来的是乱码——UTF-8 的字节被当成 Latin-1 拆开了。老办法是写成 \u4e2d\u6587 这样的 Unicode 转义(我同样试过,读出来是正确的中文),很多 IDE 会自动帮你转,但直接打开文件就成了天书。YAML 文件按 UTF-8 读取,同样的中文可以原样写,读出来也是正确的。
如果你正被 properties 里的中文困扰,可以直接把整个文件转成 YAML:用 在线转换器 转换,中文内容会原样保留。
六、转换时要检查什么
两种格式可以互相转换,但转换器只能做机械的结构变换,下面几点转完要自己看一眼:
- 数字样的字符串。我用本站转换器试过,
app.ver=3.10会输出ver: 3.1,app.zip=001234会输出zip: 1234,前导零和末尾的零都丢了。遇到版本号、邮编、工号这类值,转换后手动加上引号。 - 布尔字样和特殊字符。同样的转换器会把
on、NO、Hello: World、abc #def、@company.com自动加上单引号,这一点是可靠的。 - 多环境文件。把带
---的多文档 YAML 转成 properties 时,各段会被合并成一个文件,重复键依次出现。需要按段拆开,或者改用#---分隔。 - 键的顺序。转换器输出的键是按字母排序的,不是原文顺序,转完可能需要按阅读习惯手动调整。
七、快速决策清单
- 新项目、配置会越来越多:直接用 YAML。
- 老项目已经是 properties、运行稳定:没有明显痛点就不必为迁移而迁移。
- 有国际化资源、需要
@PropertySource额外加载、需要脚本批量改:保留 properties,或者两者并存(记得 properties 优先级更高)。 - 确定要迁移:按 《Spring Boot 配置文件从 properties 迁移到 yaml》 的步骤来,先转换、再核对、最后删旧文件。
另外,如果你的目标只是想看一份 YAML 对应的 JSON 结构,用 YAML ⇄ JSON 互转;想检查 JSON 语法,用 JSON 格式化 / 校验。
读完就动手试试:粘贴你的配置,一键转换并检查结果。