SpringBoot 外部配置文件的使用和优先级
SpringBoot 提供了灵活的外部配置机制,让我们无需重新打包就能调整应用行为。本文梳理了几种常用的外部配置方式及其优先级关系,帮你在不同部署场景下做出正确选择。
改个端口号,还要重新打包?
你一定遇到过这种场景:项目打成 jar 包部署到服务器上,结果发现端口号冲突了,或者数据库地址要换。难道就为了改一行配置,要重新走一遍构建流程?当然不用。
SpringBoot 早就考虑到了这个问题,它提供了多种外部配置方式,让同一个 jar 包可以在不同环境中灵活运行。掌握这些配置方法和它们的优先级关系,是每个 SpringBoot 开发者的必备技能。
三种配置方式对比
在深入细节之前,先看一下各方式的适用场景:
| 配置方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 命令行参数 | 临时调试、启动多实例 | 即时生效,无需改文件 | 参数多时命令行冗长 |
| 环境变量 | Docker/K8s 容器化部署 | 配置与代码解耦 | 不易管理大量配置 |
| 外部配置文件 | 生产环境、多配置项 | 集中管理,支持多环境 | 需维护文件同步 |
方式一:命令行参数
最简单粗暴的方式,直接在启动命令里加参数:
nohup java -jar myapp.jar --server.port=8081
这种方式适合临时调整,比如排查问题时临时换个端口,或者在不同终端启动多个实例。缺点也很明显——参数一多,命令行就变得又臭又长。
方式二:环境变量
在容器化部署(Docker、K8s)的场景下,环境变量是最主流的配置方式:
export SERVER_PORT=8081
java -jar myapp.jar
SpringBoot 会自动把环境变量映射到对应的配置属性上。映射规则很简单:把属性名中的点换成下划线,字母全大写。
| 配置属性 | 对应环境变量 |
|---|---|
server.port | SERVER_PORT |
spring.datasource.url | SPRING_DATASOURCE_URL |
spring.redis.host | SPRING_REDIS_HOST |
app.jwt.secret | APP_JWT_SECRET |
这种方式的好处是配置和代码完全解耦,特别适合 12-Factor App 的理念1。
方式三:外部配置文件
当需要修改的配置项比较多时,外部配置文件是最合适的选择。
SpringBoot 支持多种配置文件格式,包括 application.properties、YAML(.yml)以及 TOML(SpringBoot 3.2+)。
放在 jar 包同级目录
最省事的做法:在 jar 包所在目录创建 application.yml 或 application-{profile}.yml,SpringBoot 启动时会自动加载它,并且优先级高于 jar 包内部的同名文件。
指定配置文件路径
如果配置文件放在别的目录,用 --spring.config.location 来指定:
java -jar myapp.jar --spring.config.location=file:/path/to/application.yml
追加额外配置
有时候你不想替换默认配置,只想在默认基础上加一些覆盖项。这时候用 --spring.config.additional-location:
java -jar myapp.jar --spring.config.additional-location=file:/path/to/application-prod.yml --spring.profiles.active=prod
[!WARNING]
location和additional-location的区别很关键:location会完全替换默认的配置搜索路径,而additional-location是在默认路径之外追加。搞混了会导致很多默认配置突然失效,排查起来非常头疼。
配置加载优先级
说了这么多配置方式,那它们之间冲突了怎么办?SpringBoot 有一套明确的优先级规则(从高到低):
graph TD
A["① 命令行参数\n--server.port=9090"] --> B["② Java 系统属性\n-Dserver.port=9090"]
B --> C["③ 环境变量\nSERVER_PORT=9090"]
C --> D["④ 外部 profile 配置\napplication-prod.yml"]
D --> E["⑤ 外部默认配置\napplication.yml"]
E --> F["⑥ 内部 profile 配置\nresources/application-prod.yml"]
F --> G["⑦ 内部默认配置\nresources/application.yml"]
style A fill:#ef4444,color:#fff,stroke:none
style G fill:#3b82f6,color:#fff,stroke:none
用表格形式总结一下:
| 优先级 | 配置来源 | 示例 |
|---|---|---|
| 1(最高) | 命令行参数 | --server.port=9090 |
| 2 | Java 系统属性 | -Dserver.port=9090 |
| 3 | 操作系统环境变量 | export SERVER_PORT=9090 |
| 4 | 外部 profile 配置文件 | jar 同级目录的 application-prod.yml |
| 5 | 外部默认配置文件 | jar 同级目录的 application.yml |
| 6 | 内部 profile 配置文件 | resources/application-prod.yml |
| 7(最低) | 内部默认配置文件 | resources/application.yml |
核心原则就一句话:越「外部」的配置,优先级越高。命令行参数最高,jar 包内部的配置文件最低。同名属性会被高优先级的覆盖,不同名的则会合并。
[!NOTE]
--spring.config.location会完全替换默认的配置位置,用的时候要小心--spring.config.additional-location是追加,不会影响默认配置的加载- 后加载的配置会覆盖先加载的同名配置
如何验证哪个配置生效了?
配置来源一多,难免会遇到”我明明改了,怎么没生效”的情况。这里分享几个诊断方法。
启用 debug 模式
在启动命令里加 --debug,SpringBoot 会打印出所有配置源的加载顺序:
java -jar myapp.jar --debug
启动日志中会出现 PropertySource 相关的信息,告诉你每个配置值来自哪里。
使用 Actuator 端点
如果项目引入了 spring-boot-starter-actuator,可以通过 /actuator/env 端点查看所有生效的配置及其来源:
curl http://localhost:8080/actuator/env/server.port
[!TIP] 生产环境记得用 Spring Security 保护 Actuator 端点,不要把配置信息暴露给外部。
代码中打印
最简单粗暴的方式,在启动类里直接打印关键配置值:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
var ctx = SpringApplication.run(Application.class, args);
var env = ctx.getEnvironment();
log.info("server.port = {}", env.getProperty("server.port"));
log.info("active profiles = {}", Arrays.toString(env.getActiveProfiles()));
}
}
多环境配置最佳实践
实际项目中,通常会有 dev、test、prod 等多套环境。我的经验是这样组织:
jar 包内部只放通用的默认配置和开发环境配置:
resources/
├── application.yml # 通用默认配置
├── application-dev.yml # 开发环境(默认激活)
服务器上通过外部配置文件覆盖环境相关的值:
/opt/app/
├── myapp.jar
├── application-prod.yml # 生产环境配置(数据库、Redis 等)
启动命令:
java -jar myapp.jar --spring.config.additional-location=file:./application-prod.yml --spring.profiles.active=prod
[!IMPORTANT] 敏感配置(数据库密码、JWT 密钥等)不要写进代码仓库。生产环境的配置文件应该只存在于服务器上,或者通过配置中心(如 Nacos)统一管理。
配置管理检查清单
- jar 包内部只保留通用默认配置和开发环境配置
- 生产环境敏感配置(密码、密钥)不提交到代码仓库
- 外部配置文件放在 jar 包同级目录或通过
--spring.config.additional-location指定 - 使用
--spring.profiles.active明确指定当前环境的 profile - 通过 Actuator
/actuator/env端点验证配置是否正确生效 - Actuator 端点已配置 Spring Security 访问控制
- 多实例部署时确保配置文件版本一致,避免配置漂移
小结
回到开头的问题——改配置不需要重新打包,只需要选择合适的外部配置方式。日常开发中,我的习惯是:开发环境用 jar 包内部的默认配置,测试和生产环境通过外部配置文件覆盖,临时调试用命令行参数。理解了优先级规则,配置管理就不会乱。更多配置机制的细节可参考 Spring Boot 官方文档 - Externalized Configuration。
Footnotes
-
12-Factor App 是 Heroku 团队提出的构建 SaaS 应用的方法论,其中第三条原则「在环境中存储配置」主张将配置与代码严格分离,通过环境变量注入配置信息,从而实现同一份代码在不同环境中运行。 ↩