首页 / Spring Boot 入门教程 / YAML 进阶

Spring Boot 入门教程

YAML 进阶

本教程共 48 篇 · 第 17 篇 · 更新于 2026-08-13 · 约 2 分钟阅读

YAMLList绑定Map绑定多文档随机值占位符环境变量

本节目标:把 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 按键合并。