Spring boot属性文件加载和生效顺序深度分析

简介: spring boot最核心的特性就是自动化配置,我们在学习spring boot的时候,首要需要了解它的自动化配置原理,其次是属性文件的加载顺序,我认为这两点是学习spring boot的重中之中。

网上介绍spring boot属性文件的加载顺序的文章很多,但都没有从源码上深入分析。

今天和大家一起通过源码探究,spring boot属性文件的加载顺序。


这里先说明一点:

先加载的属性未必会生效,后加载的属性未必一定会覆盖先加载的属性值。


说明:

加载指的是将属性值读取加载到spring容器中的过程;

生效指的是运用加载到的属性值去初始化bean的过程。


属性值加载的顺序,和属性值生效的顺序没有必然的关系。


为什么要分析?


主要是在实际项目开发过程中,由于属性文件非常多,常常导致属性文件配置混乱,

最常遇到的问题就是:

为什么明明配置了属性,但项目启动就是怎么都不生效的问题?

所以,搞清楚属性文件的读取和生效的顺序,对日常基于spring boot的开发过程中非常重要。


源码分析


1、通过main启动类,找到doInvokeListener()方法建立断点,观察ApplicationEvent中属性源的变化情况:

说明:测试的工程注册在eurake中,并从配置中心config中加载属性。

63.png


2、最先加载的3个属性源

62.png

Inlined Test Properties 单元测试属性

systemProperties JVM系统属性

SystemEnvironmentProperty 系统环境变量属性


3、初始化ConfigFileApplicationListener监听器时,新加载了如下数据源

61.png

configurationProperties

配置属性源,这个属性源比较特殊,他不会去自己加载配置属性,而是将environment中已经加载到的属性源按顺序放入存放进来,并将自己放到propertySourceList的头部first。(不是很好理解,个人感觉可能是一种缓存机制)

random 随机数属性源

springCloudClientHostInfo spring cloud客户端host相关属性源

bootstrap-dev.properties

bootstrap.properties

defaultProperties


4、加载配置中心属性

60.png

可以发现,配置中心属性源configService虽然是后面加载的,但是被放在了propertySourceList的头部。


5、启动完成后,environment中propertySourceList的顺序

59.png

feign feign调用的属性源

systemProperties java se运行时系统属性

SystemEnvironmentProperty 系统环境变量属性

configService 配置中心属性源

configurationProperties 配置属性源

Inlined Test Properties 单元测试属性

random 随机数属性源

application-dev.propeties application-profile属性源

application.propeties application属性源

springCloudClientHostInfo spring cloud客户端host相关属性源

bootstrap-dev.propeties bootstrap-profile属性源

bootstrap.propeties bootstrap属性源

eureka/server.properties eureka服务属性源

defaultProperties 默认属性源


6、读取属性文件的源码分析

58.png


springboot 启动时,会初始化各种属性源PropertySource,并把加载的属性源存放到 enviroment 的propertySourceList中。我们在获取属性时,通过遍历propertySourceList的属性源去读取属性值,获取对应属性值就直接返回(先读取先生效),所以在propertySourceList前面的属性源会优先生效。


注意propertySourceList的类型是 CopyOnWriteArrayList ,即线程安全的ArrayList


7、开启属性获取日志,监控属性获取过程


logging.level.org.springframework.core.env=trace

57.png



通过注入Environment,查看执行属性加载顺序


@SpringBootTest
@Slf4j
class DemoApplicationTests {
    @Autowired
    Environment environment;
    @Test
    void getProperties() {
        System.out.println(environment.getProperty("spring.user.name"));
    }
}


properties和yml配置文件加载顺序


一般在spring boot中最好统一只使用一种类型的配置文件,避免出现配置混乱。

yml配置文件具有更好的格式,更强大的功能,但是对属性文件的配置格式要求比较。格式出错导致属性读取不到的问题比较难排查,且利用属性的键值搜索指定属性时,也不是很方便。properties属性文件,好处就是简单,不容易出现格式方面的问题,便于属性查找。

假设一个项目在同一位置同时存在application.properties和application.yml文件,


且其中都含有相同的某个key,但value不同,如:


application.properties中:

server.port=8001,


application.yml中:

server.port=8888。


问题:springboot是否都加载这两个配置文件?如果两个文件有相同的key,取哪一个文件的value?


答: 都加载,且按properties→yml的顺序加载。

56.png

在看到spring.factories中,配置加载器顺序是先执行PropertiesPropertySourceLoader再到YamlPropertySourceLoader。


在ConfigFileApplicationListener获取server.port这个key的value时候,可以发现两配置文件全都加载进去了,且注意顺序,application.properties文件在前。

55.png

getSource()方法获取到两个Source,先从application.properties文件中查找值,一旦找到立即返回,如果找不到再从application.yml中查找。


总结


1、spring boot中先读取的属性不一定先生效,生效的顺序是根据bean初始化时,environment中属性源propertySourceList的顺序来决定的。


2、属性源初始化读取的顺序和最后存放到environment中的propertySourceList的顺序没有必然关系。

environment.getPropertySources().addFirst(source);
    environment.getPropertySources().addAfter(source1,source2);
    environment.getPropertySources().addBefore(source1,source2);
    environment.getPropertySources().addLast(source);


3、属性文件的读取顺序大概是:

java se运行时系统属性 ——》系统环境变量属性——》

bootstrap.properties——》bootstrap-dev.properties ——》configService 配置中心属性源——》application属性源——》 application-dev属性源


注意:

从属性源的加载顺序就可以看出,为什么我们在微服务里面,在业务服务模块进行配置中心配置的时候,一定要在bootstrap属性文件中配置了。


4、项目中yml和properties这两个格式的配置文件,一般只选用一种类型的配置文件。

如果同时配置两种,会优先加载properties的配置文件,且properties的配置属性会优先生效。


5、属性文件的生效的顺序大致是:

java se运行时系统属性 ——》 系统环境变量属性——》

configService 配置中心属性——》application-dev属性源——》 application属性源——》bootstrap-dev.properties ——》bootstrap.properties

我们在java -jar启动脚本中配置的属性,属于 java se运行时系统属性,优先级最高。


6、如果项目中发现配置的属性一直没有生效,可以参考属性源的生效顺序,看看是否出现属性覆盖的问题。

更直接的方式是通过单元测试,执行environment.getProperty(“spring.user.name”),查看属性源列表propertySourceList的顺序,并通过打印日志来判断属性值是从具体哪个属性源加载到的。


希望看完本篇文章,当再被问到spring boot属性文件的加载顺序的时候,你可以胸有成竹的款款而谈。


https://www.jianshu.com/p/256e6019349d


目录
相关文章
|
20天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
1月前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
60 14
|
2月前
|
监控 IDE Java
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
100 8
|
2月前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
56 2
|
3月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
105 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
3月前
|
druid Java Maven
|
3月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
766 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
3月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
282 2
|
3月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
96 2
|
4月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
208 5