YAML 进阶
本教程共 48 篇 · 第 17 篇 · 更新于 2026-08-13 · 约 2 分钟阅读
本节目标:把 YAML 的看家本领学全——列表与 Map 绑定、多文档块、随机值、占位符和环境变量注入。
YAML 列表怎么写
YAML 的列表用 - 开头,同缩进的 - 属于同一个列表:
app:
servers:
- dev.example.com
- test.example.com
- prod.example.com
等价于 properties 的下标写法:
app.servers[0]=dev.example.com
app.servers[1]=test.example.com
app.servers[2]=prod.example.com
哪个好读,一眼就看得出来。这就是 YAML 的甜点。
绑定简单列表
配合第 11 章的 @ConfigurationProperties,把列表绑进 Java:
app:
servers:
- dev.example.com
- test.example.com
- prod.example.com
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private List<String> servers = new ArrayList<>();
public List<String> getServers() {
return servers;
}
public void setServers(List<String> servers) {
this.servers = servers;
}
}
绑定靠 setter,预初始化的空列表可以省掉 setter。
绑定对象列表
列表元素是复杂对象时,用嵌套类:
app:
cluster:
- ip: 127.0.0.1
path: /app1
- ip: 127.0.0.2
path: /app2
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private List<Cluster> cluster = new ArrayList<>();
public List<Cluster> getCluster() {
return cluster;
}
public void setCluster(List<Cluster> cluster) {
this.cluster = cluster;
}
public static class Cluster {
private String ip;
private String path;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
}
每个 - 元素绑成一个 Cluster 对象。列表里的字段名要对齐,写错就绑不上。
绑定 Map
配置项的名字不固定时,用 Map 接,写法如 §11 所述:Map<String, String> 属性配 @ConfigurationProperties,YAML 里每个子键绑成一个条目,之后 map.get("key") 动态取。
Map 的值也可以是对象,key 随便起:
app:
instances:
node-1:
ip: 10.0.0.1
port: 8080
node-2:
ip: 10.0.0.2
port: 8080
Note环境变量绑 Map 时,key 会被转成小写。比如
APP_URLS_API=x绑出来的是{"api": "x"}。值的大小写不受影响。
绑定 List
列表元素结构不固定时,用 Map 接最省事:
app:
props:
- name: yaml-list
url: https://example.com
- ip: 10.10.10.10
port: 8091
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private List<Map<String, Object>> props = new ArrayList<>();
public List<Map<String, Object>> getProps() {
return props;
}
public void setProps(List<Map<String, Object>> props) {
this.props = props;
}
}
每个 - 元素变成一张 Map,键值随意。
元素结构一致时还是建议建类。类型安全比省事重要。
YAML 锚点与别名
YAML 支持锚点,重复配置不用抄两遍。
& 定义锚点,* 引用它:
app:
default-timeout: &defaultTimeout 30s
order-service:
timeout: *defaultTimeout
payment-service:
timeout: *defaultTimeout
order 和 payment 的 timeout 都是 30s。改一处,两处生效。
锚点还能扩展整个映射:
db:
common: &dbCommon
driver: com.mysql.cj.jdbc.Driver
pool-size: 10
primary:
<<: *dbCommon
url: jdbc:mysql://primary/db
read-only:
<<: *dbCommon
url: jdbc:mysql://replica/db
<<: 是合并键,把锚点内容展开进当前映射。
Note锚点是 YAML 语法特性,Spring Boot 解析时支持。但别滥用,嵌套两层以上基本没人看得懂。
多文档块
一个 YAML 文件能用 --- 拆成多个文档。
每个文档独立解析,后面的文档可以覆盖前面的:
app:
name: hello-app
---
app:
name: hello-app-prod
配合 Profile 激活,一个文件装下所有环境:
spring:
application:
name: hello-app
---
spring:
config:
activate:
on-profile: dev
app:
message: 开发环境消息
---
spring:
config:
activate:
on-profile: prod
app:
message: 生产环境消息
文档从上往下处理,后定义的覆盖先定义的。
Warning多文档 properties 文件用
#---分隔,行首不能有空格。@PropertySource加载不了多文档文件,这类场景只能单文档。
随机值
配置里直接生成随机数,测试和密钥场景很实用:
app:
secret: ${random.value}
number: ${random.int}
bignumber: ${random.long}
uuid: ${random.uuid}
less-than-ten: ${random.int(10)}
in-range: ${random.int[1024,65536]}
各写法的含义:
${random.value}:随机字符串。${random.int}:随机整数。${random.int(10)}:0 到 10 之间。${random.int[1024,65536]}:1024 到 65536 之间,左闭右开。${random.long}:随机长整数。${random.uuid}:随机 UUID。
每次启动生成一次,值就固定在整个运行期。重启才变。
占位符与默认值
第 10 章讲过 ${...}。进阶用法是链式引用:
app:
name: hello-app
description: ${app.name} 是一个 Spring Boot 应用
full-description: ${app.description},作者 ${author:码上学}
:默认值 语法处理缺失属性。author 没定义时用「码上学」。
Tip占位符里写属性名,用 kebab-case 全小写,比如
${app.item-price}。这样环境变量、驼峰写法都能被识别到。
环境变量注入
容器部署的标配写法:
spring:
datasource:
url: ${DATABASE_URL:jdbc:h2:mem:default}
username: ${DB_USERNAME:sa}
password: ${DB_PASSWORD:}
环境变量缺失时,冒号后的默认值兜底。
嵌套属性也能整体注入,比如 Spring Cloud 场景常写的:
app:
config: ${APP_CONFIG}
注意 ${APP_CONFIG} 会被当成普通字符串,不是 YAML 解析。想注入结构化内容,用 SPRING_APPLICATION_JSON(第 12 章讲过)。
复杂类型的合并规则
多个来源配置同一个 List 时,是整体替换,不是追加。
app:
servers:
- a.example.com
- b.example.com
---
spring:
config:
activate:
on-profile: prod
app:
servers:
- prod.example.com
激活 prod 后,servers 只有一个元素:prod.example.com。a、b 全没了。
Map 不一样,按 key 合并。不同 key 共存,相同 key 后者赢。
规则一句话:列表整体替换,Map 按键合并。多环境配置时别指望列表自动叠加。
不能做的事
- YAML 文件不能通过
@PropertySource加载,那注解只认 properties 文件。 - 多文档 YAML 同样不能用于 @PropertySource。
- 列表和 Map 的覆盖是整体行为,没有「追加模式」。
记住这三条,能少踩不少坑。
小结
- 列表用
-,绑 List;键不固定用 Map 接。 ---拆多文档,配合spring.config.activate.on-profile做多环境。${random.*}生成随机值,支持范围写法。- 占位符支持引用、默认值、环境变量。
- 锚点
&和*消除重复配置。 - 列表整体替换,Map 按键合并。