文章目录
- [一、所有配置最终都进了同一个"桶":Environment + PropertySource](#一、所有配置最终都进了同一个“桶”:Environment + PropertySource)
- [二、PropertySource 是什么?](#二、PropertySource 是什么?)
- 三、优先级的底层规则:谁在前,谁优先
- 四、用真实打印结果,看清你项目里的最终优先级
- [五、本地文件之间的优先级:properties / yml / yaml](#五、本地文件之间的优先级:properties / yml / yaml)
- [六、Nacos 配置的优先级:和 spring.config.import 强相关](#六、Nacos 配置的优先级:和 spring.config.import 强相关)
- [七、为什么现在看不到 bootstrap.yml 了?](#七、为什么现在看不到 bootstrap.yml 了?)
- 八、实战建议:如何自己分析"谁覆盖谁"?
- 九、总结:
在 Spring Boot / Spring Cloud 项目里,我们每天都在和各种配置打交道:
application.yml / application.yaml / application.properties- Nacos 配置中心
- 命令行参数
- 系统环境变量
- JVM 启动参数
- 甚至历史上的
bootstrap.yml
但真正遇到问题时,很多人都会迷糊:
- 为什么我在 Nacos 改了值,却被本地文件"覆盖"了?
- 为什么
application.properties的优先级看起来比application.yml高? - 多个 Nacos 配置(共享的、服务私有的)谁覆盖谁?
spring.config.import里顺序一换,结果就跟着变?
这一切的根源,其实都指向同一个核心机制:
Spring 的 PropertySource 体系
只要搞懂 PropertySource 怎么存、怎么排、怎么查,配置优先级问题基本一网打尽。
一、所有配置最终都进了同一个"桶":Environment + PropertySource
Spring 有一个统一的配置入口:Environment。
无论你用的是:
- 本地
application*.yml / .yaml / .properties - Nacos 配置中心
- 命令行
--server.port=8081 - 系统环境变量
SERVER_PORT=8082 - JVM 参数
-Dserver.port=8083
最终都会被封装成一个个 PropertySource,存放在:
java
environment.getPropertySources()
这个 PropertySources 实际上就是一个有序列表,顺序非常关键。
二、PropertySource 是什么?
简单理解:
一个
PropertySource就代表一份配置来源。
常见类型有:
| 类型 | 说明 |
|---|---|
ResourcePropertySource |
从 application.yml / properties 加载 |
MapPropertySource |
普通 Map 构建的配置 |
SystemEnvironmentPropertySource |
操作系统环境变量 |
PropertiesPropertySource |
JVM 系统属性(-Dkey=value) |
CommandLinePropertySource |
命令行参数(--key=value) |
CompositePropertySource |
多个配置源组合(如某些外部配置中心) |
Spring Boot / Spring Cloud 启动时,会不断往 Environment 里"塞"各种 PropertySource,塞进去的顺序 + 插入的位置,决定了最终的优先级。
三、优先级的底层规则:谁在前,谁优先
核心规则只有一条:
PropertySources列表中,越靠前的PropertySource优先级越高。
Spring 在解析属性值时,大致是这样:
java
for (PropertySource<?> ps : environment.getPropertySources()) {
if (ps.containsProperty("some.key")) {
return ps.getProperty("some.key");
}
}
也就是说:
- 从头开始遍历
- 第一个有这个 key 的
PropertySource胜出 - 后面即便有同名 key,也完全没机会了
所以,"谁最终生效" = "谁在列表里排得更靠前"。
四、用真实打印结果,看清你项目里的最终优先级
假设项目中打印 PropertySources:
java
@Autowired
private ConfigurableEnvironment environment;
@PostConstruct
public void printSources() {
int i = 0;
for (PropertySource<?> ps : environment.getPropertySources()) {
System.out.println((i++) + " -> " + ps.getName());
}
}
你实际拿到的输出类似这样:

text
0 -> configurationProperties
1 -> CamundaBpmVersion
2 -> servletConfigInitParams
3 -> servletContextInitParams
4 -> systemProperties
5 -> systemEnvironment
6 -> random
7 -> cachedrandom
8 -> springCloudClientHostInfo
9 -> Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'
10 -> DEFAULT_GROUP@sky-dev.yml
11 -> DEFAULT_GROUP@application-dev.yml
12 -> Config resource 'class path resource [application.yml]' via location 'optional:classpath:/'
13 -> Config resource 'class path resource [application.yaml]' via location 'optional:classpath:/'
只要看这份顺序,你项目里所有配置的优先级关系就一目了然了。
结合这份结果,可以得出如下结论:
- 本地文件优先级:
text
application.properties > application.yml > application.yaml
因为:
application.properties在 index 9application.yml在 index 12application.yaml在 index 13
越靠前,优先级越高。
- Nacos 配置优先级:(import要看顺序,谁后写谁优先级高)

text
sky-dev.yml > application-dev.yml
因为:
DEFAULT_GROUP@sky-dev.yml在 index 10DEFAULT_GROUP@application-dev.yml在 index 11
也就是:服务私有配置 > 共享配置。
- Nacos vs 本地文件:
从顺序可见:
application.properties在 Nacos 之前sky-dev.yml、application-dev.yml在本地application.yml / yaml之前
所以整体顺序是:
text
(高)命令行 / JVM / 环境变量
> application.properties
> Nacos sky-dev.yml(服务私有)
> Nacos application-dev.yml(共享)
> application.yml
> application.yaml(低)
这就是你当前工程里,真实的最终优先级。
五、本地文件之间的优先级:properties / yml / yaml
从实际打印顺序可以看到,Spring Boot 对本地配置文件的加载顺序类似:
application.propertiesapplication.ymlapplication.yaml
所以当同一个 key 同时存在于这三者中时,覆盖关系是:
text
application.properties > application.yml > application.yaml
也就是说,如果你写了:
- application.yaml:
user.name.test=yaml - application.yml:
user.name.test=yml - application.properties:
user.name.test=props
最终 Environment.getProperty("user.name") 返回的是:
text
props

六、Nacos 配置的优先级:和 spring.config.import 强相关
在 Spring Boot 3 / Spring Cloud 2022 / Spring Cloud Alibaba 2023 这一套组合下,Nacos 不再通过 bootstrap.yml 加载,而是统一使用:
yaml
spring:
config:
import:
- nacos:xxx
- nacos:yyy
这里有一个非常关键的规则:
在同一个配置文件里,
spring.config.import中的多个位置:
谁写在后面,谁的优先级高(后者可以覆盖前者的同名 key)。
例如:
yaml
spring:
config:
import:
- nacos:application-dev.yml
- nacos:sky-dev.yml
application-dev.yml先导入sky-dev.yml后导入,优先级更高
冲突时:
text
sky-dev.yml > application-dev.yml
反过来写:
yaml
spring:
config:
import:
- nacos:sky-dev.yml
- nacos:application-dev.yml
则变成:
text
application-dev.yml > sky-dev.yml

结论:在同一个配置文件中,
spring.config.import的"后写的"优先级更高。
你打印出来的 PropertySources 顺序,也刚好验证了这一点:
sky-dev.yml 排在 application-dev.yml 前面,是因为在当前版本的实现中,它被处理后挂载到了更高优先级的位置 ------ 最终还是要以实际打印结果为准。
七、为什么现在看不到 bootstrap.yml 了?
历史上(Spring Cloud 2.x):
- Nacos / Config Server 等配置通常写在
bootstrap.yml - 因为 bootstrap context 比 application context 更早启动
- 会在
PropertySources中看到类似bootstrapProperties之类的条目
但在 Spring Boot 2.4+ / Spring Cloud 2021+ 之后:
- bootstrap phase 被默认移除
- 官方推荐使用新的 ConfigData 机制(
spring.config.import) - Spring Cloud Alibaba 2023 更是完全放弃了
bootstrap.yml模式
因此在现在这套版本组合下:
即使你写了
bootstrap.yml,默认也不会被加载,更不会出现在PropertySources里。
八、实战建议:如何自己分析"谁覆盖谁"?
-
启动时打印所有 PropertySource:
java@Autowired private ConfigurableEnvironment environment; @PostConstruct public void printSources() { int i = 0; for (PropertySource<?> ps : environment.getPropertySources()) { System.out.println((i++) + " -> " + ps.getName()); } } -
挑一个关键 key,定位它来自哪个 PropertySource:
java@PostConstruct public void debugKey() { String key = "user.name"; for (PropertySource<?> ps : environment.getPropertySources()) { Object val = ps.getProperty(key); if (val != null) { System.out.println("key = " + key + ", value = " + val + ", from = " + ps.getName()); break; } } }这样你能非常直观地知道:
- 某个配置到底是从哪个文件 / 哪个 Nacos 配置拿到的
- 为什么你的修改有没有生效
九、总结:
一句话记住 PropertySource 的核心逻辑
Spring Boot / Spring Cloud 所有配置优先级,最终都由
Environment.getPropertySources()的顺序决定:
列表越靠前,优先级越高;在同一个spring.config.import中,越靠后的配置优先级越高。