尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Spring Boot yml配置加载顺序与优先级实战解析

发布时间:2026/9/29 17:22:15

资讯中心
01
ARTICLE

Spring Boot yml配置加载顺序与优先级实战解析

Spring Boot yml配置加载顺序与优先级实战解析
1. yml文件的藏身之处默认搜索路径与优先级刚接触Spring Boot的时候我犯过一个很蠢的错误在IDEA里面改了application.yml重启应用配置纹丝不动。后来才反应过来jar包部署和本地run的配置加载路径根本不是一回事。Spring Boot搜索yml文件是有固定套路的搞不清楚这套规则改配置就永远像在隔山打牛。1.1 四个默认位置与外部优先原则Spring Boot启动时会按照下面四个位置依次搜索application.yml或者application.properties找到就加载没找到就继续往下找file:./config/—— 当前目录下的config子目录file:./—— 当前目录也就是java -jar执行时所在的目录classpath:/config/—— classpath根目录下的config子目录classpath:/—— classpath根目录优先级从上到下递减也就是说越靠外的位置优先级越高。这里有个非常容易忽略的点file:./config/是最高优先级它排在jar包内部的classpath:/前面。这就意味着如果你在jar包同级的config目录放了一份application.yml它就能覆盖打包进jar里的那份配置。生产中常见的做法是把配置文件放到jar包旁边的config/目录里替换掉打包进去的默认配置。运维同学改配置只需要编辑外部文件不用重新解包jar也不用重新构建项目。1.2 spring.config.name连文件名都换掉默认配置文件名是application但很多项目会遇到不想用application命名的场景。比如接入了Nacos本地兜底配置可能叫bootstrap.yml或者公司规范要求所有服务用service-{name}-config.yml这种命名。这时候用spring.config.name参数直接指定文件名前缀就行java -jar app.jar --spring.config.namemyappSpring Boot会去找myapp.yml或者myapp.properties。注意spring.config.name是可以指定多个名字的用逗号隔开。但它和spring.config.location有区别它改的是文件名而spring.config.location改的是搜索目录两者不要混淆。1.3 location参数目录与文件的精确控制如果你希望配置文件的搜索范围完全由自己掌控可以用spring.config.location。这个参数会替换掉默认的那四个位置java -jar app.jar --spring.config.locationfile:/etc/app/config/注意路径最后一定要带斜杠否则Spring Boot会认为你指定的是一个具体的配置文件名而不是目录。还有一种更稳妥的姿势是spring.config.additional-location它不会替换默认搜索位置而是在默认位置基础上追加新的目录追加的目录优先级更高java -jar app.jar --spring.config.additional-locationfile:/opt/config/我给一个实战建议线上部署时优先用additional-location而不是location。原因很简单——location一旦写了默认的四个位置全部失效万一新目录里配置文件写漏了某个参数应用就会用默认值启动容易出事故。additional-location则是既有默认兜底又能外部覆盖保留了回退的能力。注意Spring Boot 2.4之后spring.config.location的语义有了调整新增了optional:前缀概念。不带optional:时如果指定目录下没有配置文件应用会启动失败带上optional:前缀找不到配置也能继续启动。这对容器化部署很有用。2. 一张表格看懂全部配置来源的高低排序application.yml只是整个配置体系中的一环。Spring Boot真正读取配置时会同时从命令行、系统属性、环境变量、各种外部配置文件等多个来源取值这些来源之间有严格的优先级顺序。我在这个环节上栽过的跟头比改错路径多得多。2.1 最重要的十几个属性源排位Spring Boot官方定义了完整的外部化配置加载顺序从高到低简化成一张表平时够用了优先级配置来源示例最高命令行参数--server.port8081高SPRING_APPLICATION_JSON环境变量或系统属性里的JSON较高ServletConfig/ServletContext参数Web容器初始化参数中高Java系统属性-Dserver.port8081中OS环境变量SERVER_PORT8081中低jar包外部的application-{profile}.ymlconfig/application-prod.yml低jar包内部的application-{profile}.ymlclasspath里的profile配置更低jar包外部的application.yml外部基础配置最低jar包内部的application.yml打包进jar的基础配置兜底SpringApplication默认属性setDefaultProperties这条从高到低的顺序核心记忆点只有一句话离程序运行环境越远的东西优先级越高。命令行参数是运维在启动时现场敲的当然要能压过一切配置文件环境变量是部署平台比如K8s注入的也应该覆盖打包到jar里面的默认值。2.2 命令行和环境变量是怎么压过yml的我举一个自己真实遇到的场景。开发环境的application.yml里写的是server: port: 8080但测试环境用Docker部署docker run命令里加了一个环境变量docker run -e SERVER_PORT8081 app因为环境变量的优先级高于jar包内部的yml最终应用监听的是8081不是yml里的8080。一行yml都没改端口却变了这就是外部化配置的妙处。命令行参数的优先级连环境变量都压得过。调试时想临时改一下数据源又不想污染公共配置文件直接java -jar app.jar --spring.datasource.urljdbc:mysql://192.168.1.10:3306/testdb --spring.datasource.usernamereadonly这样改只对本次启动生效重启后自动恢复原配置特别适合应急排查。2.3 profile专属配置与基础配置谁覆盖谁同样两份配置文件application.yml和application-dev.yml如果两项配置都定义了server.port最终生效的是application-dev.yml里的值。规则是profile专属配置覆盖基础配置。Spring Boot的逻辑是先把application.yml基础配置加载成一份PropertySource然后把application-{profile}.yml也加载进来作为优先级更高的属性源。所以profile文件里的相同key会覆盖基础配置。这就衍生出一个挺实用的技巧公共配置放application.yml环境差异化配置放application-dev.yml、application-prod.yml。比如日志级别、超时时间这些不同环境差异很大的参数不需要复制粘贴到每个环境只需要激活对应profile就行。3. 修改yml配置的实操环境拆解、参数覆盖与加密知道了配置文件从哪里来、谁先谁后终于可以聊改了。修改yml不是只有编辑文件这一条路不同场景有不同改法选对了效率翻倍。3.1 最基础的改法直接编辑与多环境拆分单体项目最直观的改法就是编辑application.yml然后重启。但项目一多、环境一变单文件方案立刻不够用。拆分多环境Profile是最常见的做法# application.yml spring: application: name: order-service profiles: active: dev --- spring: config: activate: on-profile: dev server: port: 8080 --- spring: config: activate: on-profile: prod server: port: 8080注意Spring Boot 2.4之后推荐用spring.config.activate.on-profile来声明Profile片段旧版的spring.profiles写法在新版本里不再建议使用。启动时用--spring.profiles.activeprod切环境切换时零代码修改。3.2 运行时用命令行参数临时覆盖线上环境改配置文件需要重新发布或者至少动到服务器文件。但如果只是想临时调整一个参数命令行覆盖是成本最低的方案java -jar app.jar --server.port8082 --logging.level.com.exampleDEBUG这里有一个很多人会搞混的细节--server.port是Spring Boot特有的宽松绑定形式它是命令行参数不是Java系统属性。区别在哪# 系统属性必须放在-jar前面 java -Dserver.port8082 -jar app.jar # 命令行参数必须放在jar包后面 java -jar app.jar --server.port8082两者优先级也不同命令行参数 Java系统属性。如果你的测试环境里既有系统属性-Dserver.port8082又有命令行参数--server.port8083最终生效的是8083。3.3 yml敏感信息加密jasypt实战yml里最让人头疼的就是数据库密码、Redis密码、第三方密钥这类敏感信息。明文提交到Git仓库就是事故。我用的最多的方案是jasypt-spring-boot-starter。第一步加依赖dependency groupIdcom.github.ulisesbocchio/groupId artifactIdjasypt-spring-boot-starter/artifactId version3.0.5/version /dependency第二步配置ymljasypt: encryptor: password: ${JASYPT_ENCRYPTOR_PASSWORD} algorithm: PBEWITHHMACSHA512ANDFORGETTING_256 iv-generator-classname: org.jasypt.iv.NoIvGenerator spring: datasource: password: ENC(x7G2t5R9kQ)第三步用工具类生成密文StandardPBEStringEncryptor encryptor new StandardPBEStringEncryptor(); encryptor.setPassword(你的密钥); encryptor.setAlgorithm(PBEWITHHMACSHA512ANDFORGETTING_256); String encPwd encryptor.encrypt(真实密码); System.out.println(encPwd);用ENC()包裹密文写入yml。启动时要求环境变量JASYPT_ENCRYPTOR_PASSWORD存在否则解密失败。几个必须避开的坑加密密钥绝对不能写进yml或git用环境变量或K8s Secret注入不同jasypt版本支持的算法不一样老项目升版本后经常报PBEWithMD5AndTripleDES不支持Spring Boot 3.x必须用jasypt-spring-boot-starter 3.0.4以上版本否则启动直接报NoClassDefFoundError3.4 随机端口配置与获取实际端口随机端口是热搜词里反复出现的场景也是挺容易踩坑的一个点。yml里写server: port: ${random.int[8080,9000]}每次启动都会从8080到9000之间随机选一个端口。这个${random.int[a,b]}是Spring Boot内建的RandomValuePropertySource提供的占位符能力不需要额外引入东西。如果想让操作系统完全随机分配端口可以写server.port: 0。但有个坑写成0之后Spring Boot并没有暴露我实际监听了哪个端口日志里也只显示0。做服务注册或者联调时你需要一份代码去拿真实端口Configuration public class PortReporter { EventListener public void onWebServerInitialized(WebServerInitializedEvent event) { int actualPort event.getWebServer().getPort(); log.info(当前实际端口{}, actualPort); } }提示随机端口和固定端口混用时要特别小心。如果你的环境变量里设置了SERVER_PORT8081那yml里的${random.int[8080,9000]}根本不会生效因为环境变量优先级压过了yml里的随机占位符。这个现象我见过很多次排查时先看一下环境变量。4. 覆盖规则反直觉的三个经典踩坑现场加载顺序的规则本身不复杂复杂的是规则和现实交织之后的那些反直觉瞬间。下面这三次踩坑经历基本能代表配置覆盖问题上九成的情况。4.1 随机端口被固定的诡异问题有一次我把生产环境的yml改成server.port: ${random.int[8080,9000]}目的是想让多实例在裸机上启动时自动错开端口。结果第一个实例启动后端口是8080第二个实例启动后端口也是8080直接端口冲突。排查过程很典型我先看jar包里的application.yml确实是随机配置没问题再看启动命令没指定端口最后查Docker环境变量——SERVER_PORT8080躺在容器的环境变量列表里。原因就是前面说的优先级规则OS环境变量SERVER_PORT的优先级高于jar包内部yml里的${random.int[8080,9000]}。Spring Boot把环境变量绑定成了server.port8080根本不看yml里的随机值。解法很简单把环境变量里的SERVER_PORT删掉或者改成可覆盖的写法在yml里用占位符加默认值兜底server: port: ${SERVER_PORT:${random.int[8080,9000]}}这样如果环境变量存在就尊重环境变量如果没有就走随机。4.2 profile专属文件里写spring.profiles.active为什么不生效另一个让我困惑了很久的问题在application-prod.yml里写了spring.profiles.active: prod结果启动时没激活prod。后来才搞明白profile专属文件是在激活了对应profile之后才会被加载的你指望一个结果产物去决定产生条件逻辑上就说不通。Spring Boot 2.4之后的版本更严格如果把spring.profiles.active写在profile专属文件里启动时会直接报错。正确做法是把profile激活信息放在最基础的application.yml里或者放在启动命令和环境变量里java -jar app.jar --spring.profiles.activeprod容器化部署推荐用环境变量docker run -e SPRING_PROFILES_ACTIVEprod app4.3 用actuator的/env快速定位配置来源遇到配置覆盖说不清的时候与其猜不如直接让Spring Boot自己交代。只要引入actuator依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency然后访问GET /actuator/env接口会把当前环境中所有PropertySource按优先级排序返回每个属性后面还会标注origin直接告诉你这个值来自哪个文件哪一行。范围缩小、找到来源、确认优先级三步走完问题通常就水落石出了。如果只想看某一个具体属性可以用curl http://localhost:8080/actuator/env/server.port返回里会列出server.port在所有PropertySource中的值以及最终生效值。这个接口在生产环境必须配置好权限最好用Spring Security限制外部访问否则配置信息泄露风险很高。5. 生产级配置管理从yml到配置中心的演进经验单机版yml管理配置跑一两个服务完全OK。但当服务数量上到十几二十个之后配置管理的痛点就另一码事了。这里聊一下我对配置管理演进的实战体会。5.1 占位符与默认值给外部覆盖留一条路yml配置里用占位符加默认值的方式比直接写死值更灵活。比如server: port: ${SERVER_PORT:8080} spring: datasource: url: ${DB_URL:jdbc:mysql://localhost:3306/order_db} username: ${DB_USERNAME:order_user} password: ${DB_PASSWORD:order_pwd}这套写法的好处是默认情况直接启动就能跑不需要任何外部变量一旦部署环境注入同名环境变量立即覆盖默认值。本机开发、测试环境、生产环境同一份yml不用改一个字。这里也有个度的问题不是所有配置都需要用占位符。只有那些可能被外部覆盖的属性才需要比如端口、数据源地址、Redis地址、第三方服务URL。像spring.application.name这种很少变的写死就好全用占位符反而增加阅读成本。5.2 多环境与敏感配置的管理技巧多环境配置说白了就是两难既要环境隔离又怕重复维护。我的建议是遵循三块式结构application.yml公共配置所有环境共享只放非敏感的通用项application-{profile}.yml环境差异配置按需拆dev/test/prod敏感信息用jasypt加密或直接引用环境变量另外一定要盯住Git仓库权限。我见过不止一次有人把生产数据库密码以明文形式提交到GitLab然后被扫描工具告警。养成习惯提交前搜索一下password、jdbc:mysql这些关键字。5.3 什么时候该上配置中心当几个服务共享同一份配置时yml的各自为政模式就尴尬了。改了公共配置要逐个服务改一遍漏一个就出问题。这时候可以考虑Nacos、Consul、Spring Cloud Config这类配置中心。我用Nacos之后的感受是配置支持动态刷新不需要重启应用对存量服务平滑很多。从技术的角度yaml文件依旧是基础本地yml充当兜底配置中心作为更高优先级来源。这样即使配置中心挂了应用还能用本地配置启动。不过上配置中心不要一步到位。我的建议是先让所有服务的基础配置规范化确保本地yml可以独立启动再引入配置中心管理公共配置。否则配置中心一出问题全部服务跟着宕机压力会非常大。6. 面试高频题加载顺序怎么答才能让面试官点头这个话题出现在热搜词不是没道理的配置加载顺序确实是Java面试里被问烂了又不烂的经典题。关键区别在于有的人能背出顺序有的人能讲清楚为什么和怎么排查。后者才是面试官想要的。6.1 让优先级排序好记的一句话完整背出官方十七项顺序对大多数人来说不现实也没必要。面试的时候能说出核心原则再加几个关键节点就够了。核心原则就是外部覆盖内部运行时覆盖静态具体覆盖通用。展开说是四个要点命令行参数优先级最高因为它代表现场意图环境变量高于配置文件因为部署平台可以通过环境变量动态调整外部jar包外部优先级高于内部classpath内方便运维在不重新打包的前提下改配置profile专属配置高于基础配置application-dev.yml覆盖application.yml把这四点讲清楚面试官基本就会认可你对这套机制的理解而不只是背下来。剩下的细节用到再查就行。6.2 常见的两个面试场景推演第一个场景application.yml里配了server.port: 8080环境变量配了SERVER_PORT8081命令行传了--server.port8082问最终端口是多少答案是8082。命令行参数最高优先级。如果在Docker里配了SERVER_PORT8081但启动命令里也传了--server.port8082最终也是8082。第二个场景application.yml里配了spring.profiles.activedevapplication-dev.yml里配了server.port: 8081application.yml里配了server.port: 8080问最终端口答案是8081。dev这个profile被激活了profile专属配置的优先级更高。注意面试官可能会追问application-dev.yml里能不能配置spring.profiles.active前面讲过了不能至少Spring Boot 2.4之后不允许会直接报错。最后一个我个人的习惯性建议面试里回答这类机制性问题别急着背答案先花五秒钟想一下你实际操作中遇到的问题。面试官想听的不是课本原文而是你踩坑之后总结出来的那种理解。把真实场景带进去说服力会强很多。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。