SpringBoot如何解析${}

背景

1
2
3
spring:
datasource:
url: jdbc:mysql://${mysql_url}/device

部署pod可从环境变量中读取到mysql_url。

1
2
mqtt:
broker: tcp://${MQTT_BROKER}:3306

无法读取MQTT_BROKER。

原因

这是一个很常见但容易踩坑的问题,下面我们来逐步拆解为什么 datasource.url${} 能解析,而 mqtt.broker${} 却失败


✅ 背后机制:Spring Boot 如何解析 ${} 占位符

Spring Boot 支持三种主要的配置来源:

  1. 配置文件(如 application.yaml)
  2. 环境变量
  3. JVM 系统属性

application.yaml 中的 ${...} 占位符解析优先顺序是:

系统属性 > 环境变量 > application.yaml/.properties 本身定义的内容


✅ 你这两种配置的本质区别是:**谁来解析 ${}**?

✅ 可以注入的配置(数据源 url):

1
2
3
spring:
datasource:
url: jdbc:mysql://${secret_mysql_url}/device?allowMultiQueries=true

这是 Spring Boot 默认支持的数据源配置,它会主动读取 ${secret_mysql_url},并尝试从:

  • 环境变量 SECRET_MYSQL_URL
  • 或系统属性 -Dsecret_mysql_url=...

中查找值,自动替换

👉 通常你注入的环境变量名是大写的,比如:

1
2
3
env:
- name: SECRET_MYSQL_URL
value: "xxx"

Spring 会自动将 SECRET_MYSQL_URL 映射为 ${secret_mysql_url}(大小写不敏感 + _ 转换为 .)。

所以 ${secret_mysql_url} 是可以正常被 Spring 解析的。


❌ 无法注入的配置(mqtt broker):

1
2
mqtt:
broker: tcp://${MQTT_BROKER}:3306

这是你自定义的配置,而不是 Spring Boot 框架原生识别的结构(如 spring.datasource.url),它不会参与 Spring Boot 的属性绑定逻辑。

核心问题:

Spring 只会在加载 application.yaml 时解析 ${},但如果你把 application.yaml 作为 挂载文件,通过 SPRING_CONFIG_LOCATION 指定了这个文件路径,那么:

Spring 无法对该 YAML 中的 ${} 占位符使用 Pod 的环境变量替换(因为不会主动做这层扩展处理)。

除非你通过 spring.config.import 或使用 Spring Cloud Config 扩展机制,它才有可能解析。


解决方案

✅ 正确写法推荐(稳定方式)

为了保证自定义配置也能解析变量,请将 ${} 占位符 移出 YAML,改为注入完整值:

✅ 方法一:在 Secret 里拼好完整 URI

1
2
3
4
5
6
# Secret
stringData:
MQTT_BROKER_URI: "tcp://47.119.31.234:3306"
# application.yaml
mqtt:
broker: ${MQTT_BROKER_URI}

环境变量:MQTT_BROKER_URI → 直接注入完整值,无需在 YAML 拼接。


✅ 方法二:拼接写在 Java 代码里(不推荐)

如果你实在要使用 ${MQTT_BROKER},你可以在 Java 代码中获取并拼接:

1
2
String host = System.getenv("MQTT_BROKER");
String broker = "tcp://" + host + ":3306";

🔍 总结:为什么不行?

配置项 解析 ${} 来源 是否内置支持 能否使用环境变量
spring.datasource.url Spring Boot 自动解析 ✅ 是 ✅ 可以
mqtt.broker 只在 YAML 文件内 ❌ 否 ❌ 不行(除非配置额外支持)

✅ 推荐你使用:

1
2
mqtt:
broker: ${MQTT_BROKER_URI:tcp://localhost:1883}

然后在 Secret 中:

1
MQTT_BROKER_URI: "tcp://47.119.31.234:1883"

避免拼接,避坑保稳。

如需我帮你改成这种方式的完整 YAML,我可以一键生成。是否需要?

官方资料

🔍 官方文档中的关键点

根据 Spring Boot 的官方文档:

Spring Boot 允许你外部化配置,以便在不同的环境中使用相同的应用程序代码。你可以使用多种外部配置源,包括 Java 属性文件、YAML 文件、环境变量和命令行参数。 (docs.spring.io)

文档中还指出,Spring Boot 使用特定的 PropertySource 顺序来加载配置,后加载的配置源可以覆盖先前定义的值。(docs.spring.io)

🧩 为什么某些 ${} 占位符可以解析,而另一些不行?

application.yaml 中,Spring Boot 会解析 ${} 占位符,尝试从以下配置源中获取对应的值:

  1. 命令行参数
  2. Java 系统属性(通过 -D 参数设置)
  3. 操作系统环境变量
  4. application.propertiesapplication.yaml 文件(medium.com)

如果你的占位符引用的是环境变量,例如 ${MQTT_BROKER},Spring Boot 会尝试从环境变量中获取 MQTT_BROKER 的值。

然而,某些情况下,占位符可能无法解析,原因可能包括:

  • 环境变量未设置或名称不匹配。
  • 占位符引用的属性在配置源中不存在。
  • 使用了不支持占位符解析的配置方式。

✅ 推荐的做法

为确保占位符能够正确解析,建议遵循以下做法:

  • 确保环境变量名称与占位符中的名称完全匹配,注意大小写和命名规范。
  • 避免在 YAML 文件中进行复杂的字符串拼接,尽量将完整的值设置为环境变量,然后在配置中直接引用。
  • 使用 Spring Boot 支持的配置方式,例如通过环境变量、命令行参数或外部配置文件提供配置值。

更多详细信息,请参考 Spring Boot 官方文档中的“外部化配置”部分:(docs.spring.io)

🔗 Spring Boot Externalized Configuration