Spring Boot的配置文件加载顺序和规则

理解Spring Boot的配置文件加载顺序和规则至关重要,因为它直接影响到应用的配置覆盖和不同环境的部署。我会为你进行详细的说明,并附上具体事例。

核心概念

Spring Boot使用一个非常特别的 PropertySource 顺序来允许合理的配置覆盖。优先级从高到低排列,高优先级的配置会覆盖低优先级的配置。

配置文件加载顺序(优先级从高到低)

这个顺序是理解配置覆盖的关键。想象一下,列表顶部的配置源会"覆盖"它下方的所有配置源。

  1. DevTools全局设置 (~/.spring-boot-devtools.properties, 当激活了DevTools时)
  2. 测试环境上的 @TestPropertySource 注解
  3. 测试环境上的 properties 属性 (@SpringBootTest 注解等)
  4. 命令行参数 (e.g., java -jar app.jar --server.port=8087)
  5. 来自 SPRING_APPLICATION_JSON 的属性 (内嵌在环境变量或系统属性中的JSON)
  6. ServletConfig 初始化参数
  7. ServletContext 初始化参数
  8. JNDI属性 (来自 java:comp/env)
  9. Java系统属性 (System.getProperties())
  10. 操作系统环境变量
  11. RandomValuePropertySource (只包含 random.* 的属性)
  12. 应用 jar 包外部的 Profile-specific 配置文件 (application-{profile}.properties.yml)
  13. 应用 jar 包内部的 Profile-specific 配置文件
  14. 应用 jar 包外部的主配置文件 (application.properties.yml)
  15. 应用 jar 包内部的主配置文件
  16. @Configuration 类上的 @PropertySource 注解
  17. 默认属性 (通过 SpringApplication.setDefaultProperties 设置)

重点规则与解释

  1. Profile-specific 覆盖 非Profile-specific:

    • 一个名为 application-prod.properties 的文件(对应 prod profile)会覆盖 application.properties 主文件中的相同属性,无论它们是在 jar 包内部还是外部。这是因为Profile-specific文件的加载位置(12,13)比主配置文件(14,15)更高。
  2. 外部文件覆盖内部文件:

    • 位于 jar 包同级目录或子目录 下的 /config 文件夹中的 application.properties,会覆盖 jar 包内部application.properties。这是为什么"外部"的配置位置(12,14)优先级高于"内部"的配置位置(13,15)。
  3. 高优先级配置源覆盖低优先级配置源:

    • 命令行参数 (第4位)的优先级远高于所有配置文件(12-16)。这意味着你可以用 --server.port=8087 轻松覆盖任何配置文件中的 server.port 设置。
    • 系统属性环境变量(第9,10位)的优先级也高于所有配置文件。
  4. YAML 和 Properties 等价:

    • .properties.yml 文件的加载顺序和规则是完全一样的。YAML只是提供了更简洁的层次化表达方式。

配置文件默认搜索路径(从高到低)

当一个Spring Boot应用启动时,它会从以下位置依次加载 application.propertiesapplication.yml 文件:

  1. 当前目录的 /config 子目录 (file:./config/)
  2. 当前目录 (file:./)
  3. classpath 下的 /config (classpath:/config/)
  4. classpath 根目录 (classpath:/)

规则 :这个列表也是优先级顺序。1 位置的配置会覆盖 2, 3, 4 位置的相同配置;2 会覆盖 34,依此类推。

详细事例

假设我们有一个打包好的应用 myapp.jar,其内部和外部有以下文件结构:

复制代码
.
├── /config/
│   └── application.properties         (端口:8081)
├── application.properties             (端口:8082)
└── myapp.jar
    └── BOOT-INF/classes/
        ├── /config/
        │   └── application.properties (端口:8083)
        └── application.properties     (端口:8084)

最终生效的 server.port 是多少?

根据搜索路径的优先级:

  1. ./config/application.properties (8081) 优先级最高,它会覆盖所有其他位置的配置。
  2. 即使 jar 包内部还有配置,最终应用会使用 8081 端口。

如何覆盖?使用命令行参数:
java -jar myapp.jar --server.port=8085

此时,命令行参数 (优先级第4)覆盖了所有文件配置,最终端口是 8085


Profile-specific 配置示例

假设我们有以下文件,并且激活了 prod profile (--spring.profiles.active=prod)。

  • classpath:application.properties

    properties 复制代码
    app.name=MyApp
    db.host=localhost
    spring.profiles.active=dev # 默认激活dev profile
  • classpath:application-prod.properties

    properties 复制代码
    db.host=prod-db.example.com
    db.port=3306
  • classpath:application-dev.properties

    properties 复制代码
    db.host=dev-db.example.com

启动命令:
java -jar myapp.jar --spring.profiles.active=prod --app.name=OverriddenApp

最终生效的配置:

  1. app.name=OverriddenApp
    • 原因 :命令行参数(高优先级)覆盖了主配置文件中的 app.name=MyApp
  2. db.host=prod-db.example.com
    • 原因prod profile 配置文件覆盖了主配置文件中的 db.host
  3. db.port=3306
    • 原因 :该属性只在 application-prod.properties 中定义。
  4. 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的配置加载机制!

相关推荐
武昌库里写JAVA7 小时前
基于Spring Boot + Vue3的办公用品申领管理系统
java·spring boot·后端
我命由我123457 小时前
Android 开发 - 一些画板第三方库(DrawBoard、FingerPaintView、PaletteLib)
android·java·java-ee·android studio·安卓·android-studio·android runtime
知彼解己7 小时前
深入理解 AbstractQueuedSynchronizer (AQS):Java 并发的排队管家
java·开发语言
User_芊芊君子8 小时前
【JavaSE】复习总结
java·开发语言·python
计算机毕业设计木哥8 小时前
计算机毕业设计 基于Python+Django的医疗数据分析系统
开发语言·hadoop·后端·python·spark·django·课程设计
隔壁阿布都8 小时前
spring boot + mybatis 使用线程池异步修改数据库数据
数据库·spring boot·mybatis
Victor3568 小时前
Redis(52)Redis哨兵模式下如何进行版本升级?
后端
我有一颗五叶草8 小时前
线程间通信
java·开发语言
我真的是大笨蛋12 小时前
K8S-Pod(下)
java·笔记·云原生·容器·kubernetes