理解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
的文件(对应prod
profile)会覆盖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.properties
propertiesapp.name=MyApp db.host=localhost spring.profiles.active=dev # 默认激活dev profile
-
classpath:application-prod.properties
propertiesdb.host=prod-db.example.com db.port=3306
-
classpath:application-dev.properties
propertiesdb.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
- 原因 :
prod
profile 配置文件覆盖了主配置文件中的db.host
。
- 原因 :
db.port=3306
- 原因 :该属性只在
application-prod.properties
中定义。
- 原因 :该属性只在
spring.profiles.active=prod
- 原因 :虽然主配置文件中设置了
dev
,但命令行参数优先级更高,所以最终激活的是prod
profile。注意,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的配置加载机制!