上一章介绍的 Maven 配置决定项目使用哪些依赖、如何编译以及如何打包。应用启动以后,还需要另一类配置决定端口、日志级别和心跳接口返回的服务信息。
Spring Boot 的外部化配置让同一份代码和同一个 JAR 可以在不同环境中读取不同参数。本章先集中学习配置文件、占位符、Profile 和覆盖规则;配置怎样进入 Java 对象,将在理解 IoC 与 Spring Bean 后单独讲解。
1. 配置文件
Spring Initializr 通常会在 src/main/resources 中创建 application.properties。Maven 构建时把该目录复制到 classpath,Spring Boot 启动时自动读取其中的应用配置。
Spring Boot 也会检查 JAR 外的配置,常见位置包括应用当前目录及其 config/ 子目录。外部文件可以覆盖 JAR 内的默认值,同一个构建产物因此不必为每套环境重新编译。
常见的三个文件名是:
| 文件 | 语法 | 关系 |
|---|---|---|
application.properties | Java Properties | 使用完整的点分隔键名 |
application.yml | YAML | .yml 是 YAML 的常用扩展名 |
application.yaml | YAML | 与 .yml 使用同一种语法 |
.yml 和 .yaml 不是两种配置语言,只是文件扩展名不同。一个项目应该选择 application.properties、application.yml 或 application.yaml 中的一种作为主配置,不要在同一位置同时维护多个同名配置来源。
为了让后续章节中的文件名保持一致,心跳项目从本章开始选择 application.yaml。如果项目由 Spring Initializr 生成了空的 application.properties,可以将它改名为 application.yaml,后面只维护这一份主配置。Properties 示例仍用于讲解该语法,不需要同时复制到项目中。
2. Properties
Properties 每一行使用 键=值 表达配置,层级直接写在完整键名中:
1spring.application.name=spring2server.port=808034app.health.status=UP5app.health.service=spring
spring.application.name、server.port 是 Spring Boot 提供的配置项,app.health.* 是当前项目定义的业务配置。自定义键应使用独立前缀,避免和框架或第三方库的配置重名。
Properties 中的空行可以分隔不同主题,注释使用 # 或 !:
1# HTTP 服务2server.port=808034# 心跳接口5app.health.status=UP
Properties 没有依靠缩进表示层级,因此移动行时不容易改变结构。配置不多或者希望一眼看到完整键名时,这种格式很直接。
3. YAML
YAML 使用缩进表示层级。下面两份文件内容等价,只应选择其中一个:
01spring:02application:03name: spring0405server:06port: 80800708app:09health:10status: UP11service: spring
两格缩进表示下一层键,必须使用空格,不能混入 Tab。app.health.status 在 YAML 中被拆成 app、health、status 三层,但 Spring Environment 中的规范配置名仍然是 app.health.status。
YAML 适合层级较多的配置,也能清楚表示列表:
1app:2health:3targets:4- spring5- postgresql
冒号后的某些值可能被 YAML 解释成布尔值、数字或日期。明确需要字符串时可以加引号。缩进错误则可能把配置放到错误层级,修改后应先检查它是否仍位于预期父键下。
4. 配置项与占位符
项目当前使用下面几项配置:
| 配置项 | 示例 | 作用 |
|---|---|---|
spring.application.name | spring | 标识应用名称 |
server.port | 8080 | 设置 HTTP 监听端口 |
logging.level.root | INFO | 设置根日志级别 |
app.health.status | UP | 心跳检查的默认状态 |
app.health.service | spring | 心跳检查的默认服务 |
配置值可以使用 ${名称} 引用另一个配置,也可以用 ${名称:默认值} 提供回退值:
1spring.application.name=spring2server.port=${SERVER_PORT:8080}3app.health.status=UP4app.health.service=${spring.application.name}
SERVER_PORT 不存在时使用 8080,存在时使用环境提供的值。app.health.service 则复用应用名称,避免同一个值维护两份。
密码和令牌不应在仓库中提供可用默认值,可以只保留环境变量引用:
1app.external-api-key=${APP_EXTERNAL_API_KEY}
Spring Boot 不会仅因为项目根目录存在 .env 就自动加载它。环境变量应由终端、IDE、容器或部署平台注入。
5. Profile
Profile 用来在公共配置之上叠加环境差异。心跳项目先保留公共文件:
01spring:02application:03name: spring0405server:06port: "${SERVER_PORT:8080}"0708app:09health:10status: UP11service: "${spring.application.name}"
然后在同一目录增加环境文件:
1logging:2level:3cn.usehook.spring: DEBUG45app:6health:7service: spring-dev
激活 dev 时,Spring Boot 先加载公共文件,再使用 application-dev.yaml 中的同名配置覆盖它。Profile 文件应该与主文件保持同一种格式;如果项目选择 Properties,对应名称就是 application-dev.properties 和 application-prod.properties。
可以通过命令行或环境变量激活生产 Profile:
1java -jar target/spring-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod
不要在公共配置中固定激活生产 Profile。当前运行环境应由启动命令或部署平台决定。
6. 覆盖顺序
同一个键可能来自多个位置。常见配置来源可以从低到高理解为:
| 优先级 | 配置来源 | 示例 |
|---|---|---|
| 低 | JAR 内的应用配置 | application.yaml |
| JAR 内的 Profile 配置 | application-prod.yaml | |
| JAR 外的应用配置 | config/application.yaml | |
| JAR 外的 Profile 配置 | config/application-prod.yaml | |
| 操作系统环境变量 | APP_HEALTH_SERVICE | |
| Java 系统属性 | -Dapp.health.service=spring-system | |
| 高 | 命令行参数 | --app.health.service=spring-command |
环境变量名通常由规范键名转换而来:把点替换成下划线、移除连字符,再转换为大写。例如 app.health.service 对应 APP_HEALTH_SERVICE。
1java -jar target/spring-0.0.1-SNAPSHOT.jar \2--app.health.service=spring-command
排查配置没有生效时,应先确认激活的 Profile,再检查外部文件、环境变量、系统属性和命令行参数。不能只盯着 JAR 内的文件,因为最终值由所有属性来源共同决定。
7. 从配置值到 Java 对象
到这里,配置已经进入 Spring 的 Environment,但业务代码还没有直接读取它。本章有意停在这个边界:现在只需要理解配置来自哪里、怎样分环境以及哪个值最终生效。
下一篇先从变量、数据类型、流程控制和方法开始补齐 Java 语法,再学习类、IoC 和 Spring Bean。具备这些基础以后,「配置绑定」专章会使用 @Value 和 @ConfigurationProperties 把 app.health.* 转换成类型明确的 HealthProperties Bean,并注入 HealthService。这样配置文件语法与对象装配不会混在同一步中。