一、结论
Spring Boot 中,位置越靠后优先级越高,外部配置压倒内部配置,命令行参数拥有最高优先权。
案例:
在一次生产事故中,某团队通过 application-prod.properties
将服务端口设为 9000
,但某运维人员在启动命令中加入了 --server.port=8080
。最终服务运行在了错误端口,接口全部挂掉,业务中断近10分钟。
原因?配置优先级未搞清楚。
这篇文章将带你系统掌握:
- Spring Boot 配置文件与配置源的优先级规则
- 多环境配置加载机制与冲突排查技巧
- 常见场景下如何安全合理管理配置
二、配置优先级结构化剖析
1. 配置文件物理位置优先级
Spring Boot 启动时会自动加载配置文件,并根据物理位置顺序决定优先级。
优先级 | 文件位置 | 示例路径 | 优先级趋势 |
---|---|---|---|
1 | classpath:/ | src/main/resources/application.properties | 🡻最低 |
2 | classpath:/config/ | src/main/resources/config/ | |
3 | file:./ | 程序运行目录下的application.properties | |
4 | file:./config/ | 程序运行目录下的config/application.properties | |
5 | file:./config/*/ | 运行目录下多层子目录中的配置文件 | 🡹最高 |
举个例子:
假设配置如下:1. classpath:/application.propertiesserver.port=8080app.name=MyApp2. file:./application.propertiesserver.port=90903. file:./config/application.propertiesapp.name=ProdApp最终生效的是:
- server.port=9090(外部覆盖内部)
- app.name=ProdApp(优先级更高的 config 文件覆盖)
2. 配置源类型优先级
Spring Boot 不仅从配置文件读取属性,还可能从环境变量、命令行等多种来源加载配置。这些配置源的优先级也有先后之分。
配置源优先级图谱
关键加载顺序(由低到高):
SpringApplication.setDefaultProperties
@PropertySource("...")
- 配置文件(.properties / .yml)
- 环境变量(如
export SERVER_PORT=9090
) - JVM系统参数(
-Dserver.port=9090
) SPRING_APPLICATION_JSON
环境变量(支持JSON格式)- 命令行参数(最高优先级)
实战例子:
java -jar app.jar --server.port=9999
即使 .properties
中配置了 server.port=8080
,最终还是 9999
生效。
3. 环境配置覆盖机制(多环境配置)
Spring Boot 支持通过 profile 来管理多环境配置(如 dev/test/prod),这背后的核心是 profile-specific 配置文件加载策略。
多环境配置加载逻辑:
加载顺序如下(生效配置会覆盖默认):
application.properties
(默认配置)application-{profile}.properties
(特定环境覆盖)
激活方式三选一:
-
配置文件中指定:
spring.profiles.active=prod
-
启动参数中指定:
--spring.profiles.active=prod
-
环境变量指定:
export SPRING_PROFILES_ACTIVE=prod
多Profile叠加场景:
# application.properties
spring.profiles.active=dev,test# application-dev.properties
log.level=DEBUG# application-test.properties
log.level=INFO
最终哪个生效?后激活的profile生效(test > dev),即
INFO
。
4. 配置验证(排查工具)
当你困惑“到底哪个配置生效了?”可以用以下三种方式排查:
① Actuator /env
端点
curl http://localhost:8080/actuator/env
输出内容会告诉你每个属性的来源。
② 日志输出
Spring Boot 启动时会打印配置文件查找路径:
Config data locations:- file:./config/- file:./- classpath:/config/- classpath:/
③ 代码方式打印配置来源
@Autowired
Environment environment;@PostConstruct
public void printPropertySources() {for (PropertySource<?> ps : ((AbstractEnvironment) environment).getPropertySources()) {System.out.println("Source: " + ps.getName());System.out.println("server.port = " + ps.getProperty("server.port"));}
}
三、典型实战场景解析
场景1:开发 vs 生产配置分离
建议:
- 开发配置放在 jar 内(如
application-dev.properties
) - 生产配置放在外部
./config/application-prod.properties
- 启动时用
--spring.profiles.active=prod
激活
这样可以在不重新打包的前提下,实现灵活环境切换。
场景2:容器化部署配置(Docker/K8s)
推荐顺序:
- 命令行参数(覆盖一切)
- 环境变量(配合 K8s Secret/ConfigMap 注入)
- 文件挂载(将配置挂载至
/config/
)
样例Dockerfile:
ENV SPRING_PROFILES_ACTIVE=prod
COPY config/ /config/
ENTRYPOINT ["java", "-jar", "app.jar", "--server.port=9090"]
场景3:配置安全分层
做法建议:
- 敏感配置(如密码)通过 环境变量 注入
- 不敏感配置 保留在
.properties
文件中 - 使用
@ConfigurationProperties
+@Validated
加强类型约束
四、总结
核心口诀:
“外大于内,命大于环,动大于静”
- 外部配置 > 内部配置(jar包内)
- 命令行 > 环境变量 > 文件配置
- 动态配置源(命令、env)比静态文件优先
避坑建议:
- 避免把敏感数据写进
@PropertySource
注解中(不安全且优先级低) - 不要在多个位置重复配置同一属性,容易误判生效值
- 注意profile加载顺序,后指定的覆盖前面的
拓展思考:
如果你使用配置中心(如 Nacos、Apollo),你还需考虑:
- 配置中心刷新机制(如Spring Cloud Config的监听刷新)
- 本地配置是否允许覆盖远程配置
- 如何记录配置来源和变更历史(方便审计和回溯)
结语
掌握配置优先级,是一个成熟Spring Boot工程师的基本功。别让一行配置毁掉一条生产链路。借助本文的方法论和工具手段,你可以轻松驾驭复杂环境,让配置服务于系统,而不是系统为配置买单。