理解Spring Boot的配置文件加载顺序和规则至关重要,因为它直接影响到应用的配置覆盖和不同环境的部署。我会为你进行详细的说明,并附上具体事例。
核心概念
Spring Boot使用一个非常特别的 PropertySource 顺序来允许合理的配置覆盖。优先级从高到低排列,高优先级的配置会覆盖低优先级的配置。
配置文件加载顺序(优先级从高到低)
这个顺序是理解配置覆盖的关键。想象一下,列表顶部的配置源会"覆盖"它下方的所有配置源。
- DevTools全局设置 (
~/.spring-boot-devtools.properties, 当激活了DevTools时) - 测试环境上的
@TestPropertySource注解 - 测试环境上的
properties属性 (@SpringBootTest注解等) - 命令行参数 (e.g.,
java -jar app.jar --server.port=8087) - 来自
SPRING_APPLICATION_JSON的属性 (内嵌在环境变量或系统属性中的JSON) - ServletConfig 初始化参数
- ServletContext 初始化参数
- JNDI属性 (来自
java:comp/env) - Java系统属性 (
System.getProperties()) - 操作系统环境变量
RandomValuePropertySource(只包含random.*的属性)- 应用 jar 包外部的 Profile-specific 配置文件 (
application-{profile}.properties或.yml) - 应用 jar 包内部的 Profile-specific 配置文件
- 应用 jar 包外部的主配置文件 (
application.properties或.yml) - 应用 jar 包内部的主配置文件
@Configuration类上的@PropertySource注解- 默认属性 (通过
SpringApplication.setDefaultProperties设置)
重点规则与解释
-
Profile-specific 覆盖 非Profile-specific:
- 一个名为
application-prod.properties的文件(对应prodprofile)会覆盖application.properties主文件中的相同属性,无论它们是在 jar 包内部还是外部。这是因为Profile-specific文件的加载位置(12,13)比主配置文件(14,15)更高。
- 一个名为
-
外部文件覆盖内部文件:
- 位于 jar 包同级目录或子目录 下的
/config文件夹中的application.properties,会覆盖 jar 包内部 的application.properties。这是为什么"外部"的配置位置(12,14)优先级高于"内部"的配置位置(13,15)。
- 位于 jar 包同级目录或子目录 下的
-
高优先级配置源覆盖低优先级配置源:
- 命令行参数 (第4位)的优先级远高于所有配置文件(12-16)。这意味着你可以用
--server.port=8087轻松覆盖任何配置文件中的server.port设置。 - 系统属性 和环境变量(第9,10位)的优先级也高于所有配置文件。
- 命令行参数 (第4位)的优先级远高于所有配置文件(12-16)。这意味着你可以用
-
YAML 和 Properties 等价:
.properties和.yml文件的加载顺序和规则是完全一样的。YAML只是提供了更简洁的层次化表达方式。
配置文件默认搜索路径(从高到低)
当一个Spring Boot应用启动时,它会从以下位置依次加载 application.properties 或 application.yml 文件:
- 当前目录的
/config子目录 (file:./config/) - 当前目录 (
file:./) - classpath 下的
/config包 (classpath:/config/) - classpath 根目录 (
classpath:/)
规则 :这个列表也是优先级顺序。1 位置的配置会覆盖 2, 3, 4 位置的相同配置;2 会覆盖 3 和 4,依此类推。
详细事例
假设我们有一个打包好的应用 myapp.jar,其内部和外部有以下文件结构:
.
├── /config/
│ └── application.properties (端口:8081)
├── application.properties (端口:8082)
└── myapp.jar
└── BOOT-INF/classes/
├── /config/
│ └── application.properties (端口:8083)
└── application.properties (端口:8084)
最终生效的 server.port 是多少?
根据搜索路径的优先级:
./config/application.properties(8081) 优先级最高,它会覆盖所有其他位置的配置。- 即使 jar 包内部还有配置,最终应用会使用 8081 端口。
如何覆盖?使用命令行参数:
java -jar myapp.jar --server.port=8085
此时,命令行参数 (优先级第4)覆盖了所有文件配置,最终端口是 8085。
Profile-specific 配置示例
假设我们有以下文件,并且激活了 prod profile (--spring.profiles.active=prod)。
-
classpath:application.propertiespropertiesapp.name=MyApp db.host=localhost spring.profiles.active=dev # 默认激活dev profile -
classpath:application-prod.propertiespropertiesdb.host=prod-db.example.com db.port=3306 -
classpath:application-dev.propertiespropertiesdb.host=dev-db.example.com
启动命令:
java -jar myapp.jar --spring.profiles.active=prod --app.name=OverriddenApp
最终生效的配置:
app.name=OverriddenApp- 原因 :命令行参数(高优先级)覆盖了主配置文件中的
app.name=MyApp。
- 原因 :命令行参数(高优先级)覆盖了主配置文件中的
db.host=prod-db.example.com- 原因 :
prodprofile 配置文件覆盖了主配置文件中的db.host。
- 原因 :
db.port=3306- 原因 :该属性只在
application-prod.properties中定义。
- 原因 :该属性只在
spring.profiles.active=prod- 原因 :虽然主配置文件中设置了
dev,但命令行参数优先级更高,所以最终激活的是prodprofile。注意,spring.profiles.active本身也是一个属性,也遵循相同的覆盖规则。
- 原因 :虽然主配置文件中设置了
注意 :默认的 spring.profiles.active=dev 在主配置文件中设置,但一旦通过更高优先级的方式(如命令行)设置了profile,它就会被覆盖。Profile的激活决定了哪些 application-{profile}.properties 文件会被加载。
总结与实践建议
| 场景 | 推荐方式 | 优点 |
|---|---|---|
| 本地开发/调试 | classpath:/config/application-dev.properties |
与代码隔离,方便团队共享(不提交到git) |
| 本地临时覆盖 | 命令行参数 (e.g., --server.port=8087) |
最方便快捷 |
| 服务器通用配置 | Jar包外部的 ./config/application.properties |
优先级高,与Jar包分离,更新应用时不影响配置 |
| 服务器环境特定配置 | Jar包外部的 ./config/application-prod.properties |
同上,并且与环境绑定 |
| 敏感信息(密码等) | 绝不写在配置文件中。使用OS环境变量或启动参数传递。 | 安全,不会意外泄露 |
希望这份详细的解释和示例能帮助你彻底掌握Spring Boot的配置加载机制!