Spring Boot的核心配置文件一览及其加载顺序

Spring Boot 启动时会去好几个地方翻 application.properties,文件本身还有 .yml 版本、profile 版本,再往上还有命令行参数、环境变量、系统属性。这些来源最后是按固定顺序叠起来的,同一个 key 谁在后谁生效。

有哪些配置文件

文件 说明
application.properties / application.yml / application.yaml 主配置,默认名字是 application
application-{profile}.properties / .yml profile 专用,-dev、-prod 都是这个套路
bootstrap.properties / bootstrap.yml Spring Cloud 用的,父上下文,比 application 还早
spring.config.import=... 指向的文件 手动引进来的普通文件、configtree 目录
@PropertySource("classpath:xxx.properties") 挂在 @Configuration 类上
~/.config/spring-boot/spring-boot-devtools.properties devtools 的全局配置,对所有项目生效
@TestPropertySource / @SpringBootTest(properties = {...}) 只在测试里生效

bootstrap.properties 在新版本里默认已经不加载了。Spring Boot 2.4 之后推荐用 spring.config.import=configserver: 代替,想继续用 bootstrap 得单独加 spring-cloud-starter-bootstrap 这个依赖,否则那个文件放在那儿也不会被读。


默认找哪些位置

不写任何配置的情况下,Boot 会依次去这五个位置找(ConfigDataEnvironment 里写死的):

text 复制代码
optional:classpath:/
optional:classpath:/config/
optional:file:./
optional:file:./config/
optional:file:./config/*/

file:./ 是运行目录 ,不是 classpath,也不是 IDEA 的模块目录。file:./config/*/ 里的 * 匹配 config/ 下的一级子目录。

越靠后的位置优先级越高。 所以 file:./config/ 里的配置能盖掉 classpath:/ 里的。

在五个位置各放一个 application.properties,每个里面写 loc=xxx,启动后把 Environment 里的属性源打出来:

text 复制代码
    Config resource 'file [config/sub/application.properties]' via location 'optional:file:./config/*/'
    Config resource 'file [config/application.properties]' via location 'optional:file:./config/'
    Config resource 'file [application.properties]' via location 'optional:file:./'
    Config resource 'class path resource [config/application.properties]' via location 'optional:classpath:/config/'
    Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'

这个列表是从高到低排的,最后 loc 的值是 file:./config/sub/ 里那个。

file:./config/*/ 是 2.4 才加进来的,* 匹配到的子目录全部 都会被加载(比如 config/a/、config/b/ 各放一份),按目录名排序,同样越靠后的越高。实测两个子目录分别写 sub=a、sub=b,最后生效的是 b:

text 复制代码
    Config resource 'file [config/b/application.properties]' via location 'optional:file:./config/*/'
    Config resource 'file [config/a/application.properties]' via location 'optional:file:./config/*/'

同一个目录里的优先级

同一个目录下,能同时存在三种文件:

text 复制代码
application-{profile}.properties   ← 最高
application.properties
application.yml

.properties 压 .yml,profile 专用压普通。这两条都是实测过的,把 application.properties 和 application.yml 都放上:

properties 复制代码
# application.properties
foo=from-properties
yaml 复制代码
# application.yml
foo: from-yml
text 复制代码
>>> foo = from-properties

属性源列表里也是 properties 在前:

text 复制代码
    Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'
    Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'

这两个文件不是"二选一",都会被加载。.yml 里独有的 key 照样读得到,只有冲突的 key 才会被 properties 盖掉。


profile 专用文件的优先级

网上很多说法是"application-{profile} 优先级最高",这话只对一半。

在同一个位置 里,application-{profile} 确实压过 application。但跨位置就不一定了。实测四组(Spring Boot 3.4.4,同一个 key 写在两边,看谁生效):

classpath:/ classpath:/config/ file:./ file:./config/ file:./config/*/ 结果
application-dev.properties application.properties classpath:/ 的 dev 赢
application-dev.properties application.properties file:./config/ 赢
application-dev.properties application.properties file:./ 赢
application-dev.properties application.properties file:./ 的 dev 赢

规律是:profile 专用文件会被提到自己所属的那个根(classpath: 或 file:)里所有普通文件之上,但 file 根整体还是压着 classpath 根。

text 复制代码
file 根:   file:./application-dev.properties
            file:./config/*/application.properties
            file:./config/application.properties
            file:./application.properties
classpath 根:classpath:/application-dev.properties
            classpath:/config/application.properties
            classpath:/application.properties
            classpath:/application.yml

所以 classpath 上的 application-dev.properties 打不过 file:./config/application.properties。打包成 jar 之后 profile 配置就在 classpath 里,file:./config/ 是运维能直接改的地方,想盖掉它只能靠位置更高的普通文件。


完整的属性源顺序

上面只说了配置文件。真正决定"最终生效的是哪个值"的是完整的属性源列表,从低到高:

# 来源
1 SpringApplication.setDefaultProperties(...) 设的默认值
2 @Configuration 类上的 @PropertySource
3 配置文件(上面那五个位置找出来的全部)
4 random.*
5 操作系统环境变量
6 Java 系统属性(-Dxxx=yyy)
7 JNDI(java:comp/env)
8 ServletContext 初始化参数
9 ServletConfig 初始化参数
10 SPRING_APPLICATION_JSON
11 命令行参数(--server.port=8081)
12 测试注解的 properties 属性
13 测试里的 @DynamicPropertySource
14 测试上的 @TestPropertySource
15 devtools 全局配置(~/.config/spring-boot/ 下)

把 Environment 里的属性源打出来大概长这样:

text 复制代码
    configurationProperties
    commandLineArgs
    systemProperties
    systemEnvironment
    random
    Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'
    Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'
    applicationInfo

这是非 web 应用的输出,所以 JNDI、ServletContext 那几项没出现。configurationProperties 是 Spring 自己加的包装,不是真实来源。commandLineArgs 压 systemProperties、systemProperties 压 systemEnvironment,和上面表里的顺序对得上。

这张表能直接解释几个常见现象:

  • --server.port=8081 一定能盖掉配置文件里的端口,因为命令行参数排第 11
  • 容器里配 SPRING_DATASOURCE_URL 环境变量能盖掉 jar 里的配置,因为环境变量排第 5,比配置文件高
  • @PropertySource 里的值会被 application.properties 覆盖,因为它排第 2,比配置文件还低

环境变量名怎么映射到 key 上(SPRING_DATASOURCE_URL → spring.datasource.url)属于 relaxed binding 的内容,这里不展开。


几个容易混的点

spring.profiles.active 能写在哪儿

写在 application.properties 里是可以的,不管这个文件在哪个位置,Boot 在激活 profile 的那一步就会读它,实测生效。

写在 profile 专用文件里不行,直接启动失败:

text 复制代码
org.springframework.boot.context.config.InvalidConfigDataPropertyException:
Property 'spring.profiles.active' imported from location 'class path resource [application-dev.properties]' is invalid in a profile specific resource

原因是这个属性决定"要加载哪些 profile 专用文件",如果它自己出自一个 profile 专用文件,就变成先有鸡还是先有蛋。同理,spring.profiles.include、spring.config.activate.on-profile 也不能写在这里面。

spring.config.location 和 additional-location

一个替换、一个追加:

spring.config.location spring.config.additional-location
效果 替换掉默认的五个位置 在默认位置之外追加
优先级 只剩指定的这几个位置 指定的位置 > 默认的五个位置

写成 spring.config.location=file:./extra/,默认位置就全没了,classpath 里的 application.properties 都不会被加载。实测:

text 复制代码
>>> foo = null          # classpath:/application.properties 里的值读不到了
>>> baz = null          # application.yml 一样读不到
>>> ext = from-extra    # 只有 file:./extra/application.properties 生效

而且这两个属性不能写在 application.properties 里,写在那里不会生效:

text 复制代码
>>> ext = null          # application.properties 里写了 spring.config.additional-location=file:./extra/,没读
>>> ext = from-extra    # 改成命令行 --spring.config.additional-location=file:./extra/ 传,生效

因为"去哪儿找配置文件"这件事必须在读配置文件之前就定下来,它只能来自比配置文件更高优先级的来源:命令行参数、系统属性、环境变量。spring.config.name 同理,想改名只能从这几处改。

spring.config.import

spring.config.import 可以把任意文件引进来,写法和优先级是反直觉的:

properties 复制代码
# application.properties
spring.config.import=optional:file:./custom.properties
text 复制代码
    Config resource 'file [custom.properties]' via location 'optional:file:./custom.properties'
    Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'

被 import 进来的 custom.properties 排在引用它的文件上面 ,也就是说它优先级更高。optional: 前缀表示文件不存在也不报错,不加的话找不到文件直接启动失败:

text 复制代码
Config data resource 'file [no-such-file.properties]' via location 'file:./no-such-file.properties' does not exist

Action: Check that the value 'file:./no-such-file.properties' at class path resource
[application.properties] - 2:22 is correct, or prefix it with 'optional:'

configtree: 是另一种写法,专门读 K8s 那种一个 key 一个文件的目录:

properties 复制代码
spring.config.import=configtree:/etc/config/

list 是整体替换

properties 复制代码
# application.properties
mylist[0]=a1
mylist[1]=a2
mylist[2]=a3
properties 复制代码
# application-dev.properties
mylist[0]=b1

激活 dev 之后绑出来的结果是:

text 复制代码
>>> mylist = [b1]

不是 [b1, a2, a3]。列表类型的属性,高优先级来源会把整个列表替换掉 ,@ConfigurationProperties 里的 List、Map 都是这个行为。想让基础配置保留一部分元素,只能把整个列表在同一个地方写全。

.yml 里的多文档

一个 .yml 文件里可以用 --- 分成多段,配合 spring.config.activate.on-profile 做到"一个文件管多个环境":

yaml 复制代码
server:
  port: 8080

---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8081

这里用的是 spring.config.activate.on-profile,不是 spring.profiles.active。后者只有一种用法,就是指定这次要激活哪些 profile,写在 application.properties、命令行或者环境变量里。

spring.factories 不是配置文件

META-INF/spring.factories(2.7 之前)和 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(2.7 之后)名字里带 spring、也在 META-INF 下,经常被当成配置文件。它们是框架自己的注册表 ,用来登记自动配置类、EnvironmentPostProcessor、PropertySourceLoader 这些东西,和 application.properties 完全不是一回事,也不参与上面那套优先级。

相关推荐
zhangx1234_41 分钟前
javaEE 多线程1
java·linux·服务器
鬼手点金1 小时前
opencode-crawl4ai使用建议
java·开发语言·人工智能·python·机器学习
高频因子挖掘机1 小时前
量化选股到底要不要每天拉全市场股票?从全市场扫描到候选池的行情数据设计
后端·github·api
专业程序开发源1 小时前
springboot校园志愿者管理系统28562-计算机课程设计、毕业设计
java·spring boot·后端·python·django·php·课程设计
thefool1122661 小时前
多线程 2
java
谢亮_vipxieliang1 小时前
Go select 多路复用:从语法到实战的完整指南
开发语言·后端·golang
高频因子挖掘机1 小时前
复权价格怎么算?从除权因子、时间方向到量化回测避坑
后端·github·api
mldong1 小时前
一条审批流的数据库账:5 张核心表、3 张扩展表、0 张表单表
后端·架构
daidaidaiyu11 小时前
ThingsBoard 集群的核心逻辑源码分析
java