Spring Boot 配置文件到底加载哪一个?4 个默认位置、优先级与覆盖规则一次讲透
很多 Spring Boot 初学者都遇到过这种情况:明明修改了
application.yml,启动后却没有生效;同一个属性在多个文件中出现,最终值到底来自哪里;项目打成 JAR 后,又该如何在不重新打包的情况下修改端口?本文通过一个可以亲手复现的 Demo,讲清楚 Spring Boot 默认配置文件的四个加载位置、覆盖优先级、互补配置以及最容易踩的坑。
一、实验环境
本文示例基于以下环境:
| 工具 | 版本 |
|---|---|
| JDK | 1.8 |
| Spring Boot | 2.0.2.RELEASE |
| Maven | 3.x |
| 配置格式 | YAML |
说明:本文严格按照 Spring Boot 2.0.2.RELEASE 的行为进行演示。Spring Boot 2.4 以后引入了新的 Config Data 机制,升级项目时应同时查看对应版本的官方文档。
二、先记住结论:四个默认位置
Spring Boot 启动时,会按照优先级从高到低扫描以下位置:
| 优先级 | 配置位置 | 含义 |
|---|---|---|
| 1 | file:./config/ |
当前运行目录下的 config 目录 |
| 2 | file:./ |
当前运行目录 |
| 3 | classpath:/config/ |
类路径下的 config 目录 |
| 4 | classpath:/ |
类路径根目录 |
可以简化记忆为:
项目外部 > 项目内部
config 目录 > 根目录
完整优先级:
file:./config/
↓
file:./
↓
classpath:/config/
↓
classpath:/
这里有两个非常重要的概念。
1. 什么是 classpath?
对于 Maven 项目,下面的目录会进入类路径:
src/main/resources
因此:
src/main/resources/application.yml
对应:
classpath:/application.yml
而:
src/main/resources/config/application.yml
对应:
classpath:/config/application.yml
2. 什么是 file:./?
file:./ 表示程序启动时的当前工作目录。
如果在 IDEA 中运行,通常是项目根目录;如果使用以下命令运行 JAR:
java -jar springBootDemo.jar
那么当前终端所在的目录就是 file:./。
三、准备一个可复现的测试接口
为了准确观察属性来自哪个配置文件,我们定义两个属性:
config-demo:
source: classpath-root
author: qcby
在 controller 包中创建 ConfigLocationController:
package com.qcby.springBootyml.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigLocationController {
@Value("${config-demo.source}")
private String source;
@Value("${config-demo.author}")
private String author;
@GetMapping("/config-location")
public String configLocation() {
return "配置来源:" + source + ",作者:" + author;
}
}
本文使用的端口为:
server:
port: 8088
启动项目后访问:
http://localhost:8088/config-location
四、实验一:classpath 根目录
在以下位置创建配置文件:
src/main/resources/application.yml
内容:
server:
port: 8088
config-demo:
source: classpath-root
author: qcby
启动项目,接口返回:
配置来源:classpath-root,作者:qcby
当前只有一个配置来源,因此没有发生覆盖。
五、实验二:classpath 的 config 目录
创建:
src/main/resources/config/application.yml
只配置 source:
config-demo:
source: classpath-config
重新启动后访问接口,结果变成:
配置来源:classpath-config,作者:qcby
为什么 source 变了,而 author 还在?
classpath:/config/的优先级高于classpath:/,所以source被覆盖。- 高优先级文件没有配置
author,所以继续使用低优先级文件中的值。
这就是 Spring Boot 的两个核心规则:
相同属性:高优先级覆盖低优先级
不同属性:多个配置来源形成互补
六、实验三:当前项目根目录
在项目根目录创建 application.yml:
springBootDemo
├─ application.yml ← 新建在这里
├─ pom.xml
└─ src
内容:
config-demo:
source: file-project-root
重新启动后访问接口:
配置来源:file-project-root,作者:qcby
这说明项目外部的 file:./application.yml 覆盖了类路径中的配置。
IDEA 中没有生效怎么办?
打开:
Run → Edit Configurations
确认 Working directory 是:
$PROJECT_DIR$
如果工作目录不是项目根目录,Spring Boot 自然无法在预期位置找到外部文件。
七、实验四:当前目录的 config 目录
在项目根目录创建:
config/application.yml
目录结构:
springBootDemo
├─ config
│ └─ application.yml ← 默认位置中优先级最高
├─ application.yml
├─ pom.xml
└─ src
配置内容:
config-demo:
source: file-config
重新启动,接口返回:
配置来源:file-config,作者:qcby
至此,四个位置的优先级已经全部验证完毕。
八、最终合并结果是如何产生的?
假设四个配置文件内容如下:
classpath:/application.yml
config-demo:
source: classpath-root
author: qcby
classpath:/config/application.yml
config-demo:
source: classpath-config
file:./application.yml
config-demo:
source: file-project-root
file:./config/application.yml
config-demo:
source: file-config
Spring Boot 最终得到的配置相当于:
config-demo:
source: file-config
author: qcby
source 使用最高优先级的值,author 则从低优先级配置中补充。
所以 Spring Boot 并不是简单地“选择一个配置文件”,而是将多个 PropertySource 合并后,再按照优先级决定同名属性的最终值。
九、为什么外部配置特别重要?
打包之后,类路径中的配置会进入 JAR:
src/main/resources/application.yml
↓ Maven 打包
BOOT-INF/classes/application.yml
如果要修改 JAR 内部配置,通常需要重新打包。
而外部配置不在 JAR 中:
deploy
├─ config
│ └─ application.yml
└─ springBootDemo.jar
部署时可以直接修改外部配置:
server:
port: 9000
然后重新启动 JAR,无需重新编译代码:
java -jar springBootDemo.jar
这也是开发、测试、生产环境使用同一份 JAR 的基础。
十、Profile 与配置位置如何配合?
如果激活 prod:
--spring.profiles.active=prod
Spring Boot 还会查找:
application-prod.yml
在 Spring Boot 2.0.2 中,可以理解为:
JAR 外部的 Profile 配置
> JAR 内部的 Profile 配置
> JAR 外部的普通配置
> JAR 内部的普通配置
例如,可以把通用默认值放进 JAR:
server:
port: 8088
再在服务器外部提供生产配置:
server:
port: 9000
这样既能保留默认值,又能根据部署环境覆盖关键参数。
十一、最容易踩的 7 个坑
1. 把外部配置误放进 resources
下面是类路径配置:
src/main/resources/config/application.yml
下面才是外部配置:
项目根目录/config/application.yml
两者不是同一个位置。
2. 忽略 IDEA 的 Working directory
file:./ 取决于工作目录,而不是 Java 文件所在目录。
3. 认为只会加载一个 application.yml
Spring Boot 会加载多个配置来源。同名属性覆盖,不同属性互补。
4. YAML 缩进错误
正确:
config-demo:
source: file-config
错误:
config-demo:
source:file-config
冒号后必须有空格,并且不要使用 Tab 缩进。
5. 同时使用 application.yml 和 application.properties
虽然 Spring Boot 支持两种格式,但不建议在同一位置同时维护两份同名配置,否则排查属性来源会变得困难。
6. 修改 resources 后没有重新构建
如果怀疑 target/classes 中的资源没有更新,可以执行:
mvn clean package
7. 忽略命令行参数的优先级
即使文件中配置:
server:
port: 8088
下面的命令仍会将端口覆盖为 9000:
java -jar springBootDemo.jar --server.port=9000
在 Spring Boot 2.0.2 中,命令行参数的优先级高于 application 配置文件。
十二、面试时怎么回答?
如果面试官问:“Spring Boot 默认从哪些位置加载配置文件,优先级如何?”
可以回答:
Spring Boot 默认会从当前目录的
config子目录、当前目录、类路径的config目录以及类路径根目录加载application.properties或application.yml。优先级从高到低依次是file:./config/、file:./、classpath:/config/、classpath:/。多个配置来源会形成互补配置,同名属性由高优先级覆盖低优先级。
如果继续追问为什么要这样设计,可以回答:
类路径配置用于提供项目默认值,外部配置用于部署时覆盖默认值。这样可以让同一个 JAR 在不同环境中运行,而不需要重新修改和打包代码。
十三、总结
本文最重要的结论只有三条:
- 外部配置的优先级高于 JAR 内部配置。
config目录的优先级高于同级根目录。- 相同属性会被覆盖,不同属性会形成互补。
再记一次完整顺序:
file:./config/
> file:./
> classpath:/config/
> classpath:/
掌握这个顺序后,绝大多数“为什么我的 application.yml 没生效”的问题都能快速定位。
参考资料
推荐搜索关键词: Spring Boot 配置文件优先级、application.yml 加载顺序、Spring Boot config 目录、外部配置、classpath application.yml、Spring Boot 配置覆盖。
更多推荐
所有评论(0)