Spring Boot v2.4.4源码解析配置文件加载篇一 #2.4版本与2.3版本差异
Posted u一枚蒟蒻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot v2.4.4源码解析配置文件加载篇一 #2.4版本与2.3版本差异相关的知识,希望对你有一定的参考价值。
Spring Boot 2.4版本与2.3版本处理配置文件差异
一、概述
从Spring Boot v2.4.4源码解析(三)事件机制篇一这篇博客中可以看出,在Spring Boot启动过程中包含很多关键时间节点,EventPublishingRunListener
会将这些时间节点封装成事件并通过SimpleApplicationEventMulticaster
广播出去。
在这些时间节点中,有一个environmentPrepared
,表示环境已经准备好,如果需要对环境做定制化操作的组件,可以监听该事件。Spring Boot正是在该时间节点将配置文件加载到环境Environment
的propertySources
属性中。
在事件广播器SimpleApplicationEventMulticaster
广播事件方法multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType)
内部打断点,断点条件是event instanceof org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
,表示发布ApplicationEnvironmentPreparedEvent
事件时进入断点。
然后在Variables中添加getApplicationListeners(event, type)
查看有哪些事件监听器监听ApplicationEnvironmentPreparedEvent
事件。
在Spring Boot 2.3版本和2.4版本中会发现有如下ApplicationEnvironmentPreparedEvent
事件监听器:
二、变更原因
注意,2.3版本中ConfigFileApplicationListener
和2.4版本中EnvironmentPostProcessorApplicationListener
正是处理配置文件的事件监听器。
为什么Spring Boot 2.4版本要放弃ConfigFileApplicationListener
而使用EnvironmentPostProcessorApplicationListener
处理配置文件加载呢?
Config file processing in Spring Boot 2.4这篇博客给出了答案。
- 支持Kubernetes卷挂载配置;
卷挂载配置是 Kubernetes 的一个流行功能,其中ConfigMap可以将环境配置信息和容器镜像解耦便于应用配置的修改。
这块内容可以参考K8s 集群使用 ConfigMap 优雅加载 Spring Boot 配置文件。 ConfigFileApplicationListener
本身存在一些问题;
在Config file processing in Spring Boot 2.4文章中, 作者将ConfigFileApplicationListener
比喻为陷阱冒险(trap adventures),并不是指其代码质量不高,或者测试覆盖不全面,而是指很难添加新功能。
在处理特定profile配置文件(主要YAML文件)会遇到两个问题:- 可以在这些配置文件中激活附加的profiles;
- 很难弄清楚配置文件的添加顺序;
例如,在YAML文件中可以包含多个逻辑文件,这些逻辑文件之间用---
分隔开。考虑如下包含三个逻辑文件的YAML文件:
security.user.password: usera
---
spring.profiles: local
security.user.password: userb
runlocal: true
---
spring.profiles: prod
spring.profiles.include: local
security.user.password: userc
在Spring Boot 2.3中添加VM参数-Dspring.profiles.active=prod
并启动,获取security.user.password
和runlocal
配置,会发现其值分别为userb
和true
。是不是感觉有点奇怪,在处理中间文件时,该文件的profiles还未被激活,但是最终该文件还是被包含进来了。
所以在Spring Boot 2.4中对加载properties和YAML配置文件方式作出了两个重大改变,旨在简化和合理化加载外部配置的方式:
- 将按照定义顺序加载文件;
- 不能在特定profile文件中激活profiles;
三、主要变更点
1. YAML 多逻辑文件顺序变更
对于Spring Boot 2.3及更早版本, YAML中的多个逻辑文件基于profile激活顺序添加,而在2.4版本中,则按照文件声明顺序添加。
例如,对于如下YAML文件:
test: "value"
---
test: "overridden-value"
最终test
属性值为overridden-value
;
2. 增加 Properties 文件多逻辑文件
在 Spring Boot 2.4 中,在Java properties文件中可以使用和YAML类似的多逻辑文件,多个逻辑文件之间用#---
隔开。
例如:
test=value
#---
test=overridden-value
3. Jar 包外部配置文件和内部配置文件顺序变更
在Spring Boot 2.3及更早版本中,Jar包外部的application.properties
文件不会覆盖Jar包内部的application-<profile>.properties
文件。
从Spring Boot 2.4开始,外部文件总是覆盖Jar包内部文件(application-<profile>.properties
文件或者其他文件);
具体配置文件加载顺序可以参考Application properties outside of jar should take precedence over profile-specific variants inside the jar,Spring Boot Reference Documentation 2.3.6.RELEASE,Spring Boot Reference Documentation 2.4.1。
Spring Boot 2.3及更早版本配置文件优先级(从上到下优先级降低):
- Jar包外部的
application-{profile}.properties
文件及application-{profile}.yml
; - Jar包内部的
application-{profile}.properties
文件及application-{profile}.yml
; - Jar包外部的
application.properties
及application.yml
; - Jar包内部的
application.properties
及application.yml
;
Spring Boot 2.4配置文件优先级(从上到下优先级降低):
- Jar包外部的
application-{profile}.properties
文件及application-{profile}.yml
; - Jar包外部的
application.properties
及application.yml
; - Jar包内部的
application-{profile}.properties
文件及application-{profile}.yml
; - Jar包内部的
application.properties
及application.yml
;
4. spring.profiles
配置项迁移
在Spring Boot 2.3及更早版本中,可以在YAML的多逻辑文件中使用spring.profiles
指定该配置文件生效的profile,在2.4版本中,将该配置项迁移到spring.config.activate.on-profile
。
5. Profile 激活
Spring Boot中可以使用spring.profiles.active
配置项激活特定的profiles,例如可以使用如下命令行激活prod
:
$ java -jar myapp.jar --spring.profiles.active=prod
当然也可以在application.properties
或者application.yaml
文件中配置该配置项,但是在Spring Boot 2.4中,不能在特定profile文件中设置该配置项值。也就是说,在Spring Boot 2.4中,配置项spring.config.activate.on-profile
和spring.profiles.active
不能同时设置。类似地,配置项spring.config.activate.on-profile
和spring.profiles.include
也不能同时设置。
例如,如下配置将会报错:
spring:
config:
activate:
on-profile: "prod"
profiles:
include: "metrics"
为什么会增加这样一个限制呢?
加了这个限制后,同一个文件中on-profile
条件只会计算一次,前文这个问题就会解决。
6. Profile 组
由于Spring Boot 2.4不能同时设置spring.config.activate.on-profile
和spring.profiles.active
,也不能同时设置spring.config.activate.on-profile
和spring.profiles.include
,那对于2.3版本使用spring.profiles
+ spring.profiles.include
扩展激活profiles这种情况怎么兼容呢?
例如,如下application.yaml
:
spring.profiles: "debug"
spring.profiles.include: "debugdb,debugcloud"
如果迁移到Spring Boot 2.4 application.yaml
变更为:
spring:
config:
activate:
on-profile: "debug"
profiles:
include: "debugdb,debugcloud"
但是不能同时设置spring.config.activate.on-profile
和spring.profiles.include
。
为了解决这类问题,Spring Boot 2.4 提出Profile组概念,Profile组可以完成这样一件事:如果profile 'x’处于激活状态,profiles ‘y’ 和 ‘z’ 也处于激活状态。配置项格式为spring.profiles.group.<source>
,例如,上面的配置可以这样写:
spring:
profiles:
group:
"debug": "debugdb,debugcloud"
同样,也不能同时设置配置项spring.profile.group
和spring.config.activate.on-profile
的值。
7. 导入附加配置
配置文件加载成功后,会以OriginTrackedMapPropertySource
形式存在Environment
的PropertySources
属性中。可以在Spring Boot启动类中增加如下代码,查看引入了哪些配置文件:
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
propertySources.forEach( s -> {
if(s instanceof OriginTrackedMapPropertySource){
System.err.println(s);
}
});
在Spring Boot 2.3版本中,配置文件默认搜索顺序为(从上到下优先级降低):
file:./config/
;file:./config/*/
;file:./
;classpath:/config/
;classpath:/
;
在Jar包内部resources目录,Jar包内部resources/config目录,Jar包当前目录,Jar包当前/config目录,Jar包当前/config/fsx目录下面分别放置application.yml
,然后以java -jar target/demo-0.0.1-SNAPSHOT.jar
命令行运行该Jar包,会发现如下输出:
OriginTrackedMapPropertySource {name='applicationConfig: [file:./config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [config/fsx/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}
搜索配置文件名称默认为application
,可以通过配置项spring.config.name
配置,也可以使用配置项spring.config.location
明确指定配置文件搜索顺序(多个文件或者目录质检用逗号隔开)。
例如,以命令行java -jar target/demo-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:/,file:./
启动时,输出如下:
OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}
可以通过spring.config.additional-location
在默认的搜索顺序上增加搜索路径,该配置项的值优先默认的搜索顺序。
在Jar包路径/custom-config目录下添加application.yml
文件,并以命令行java -jar target/demo-0.0.1-SNAPSHOT.jar --spring.config.additional-location=file:./custom-config
启动时,输出如下:
OriginTrackedMapPropertySource {name='applicationConfig: [file:custom-config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [config/fsx/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}
此时,配置文件默认搜索顺序为(从上到下优先级降低):
file:./custom-config/
;classpath:custom-config/
;file:./config/
;file:./config/*/
;file:./
;classpath:/config/
;classpath:/
;
但有一个问题,这几个配置项必须配置为环境属性,例如操作系统环境变量,系统变量,命令行参数,不能中application.properties
或者 application.yml
中指定,且文件名仅限于.properties
, .yaml,
或者.yml
。
在Spring Boot 2.4 中新增了配置项spring.config.import
,可以使用该配置项导入附加配置, 且该配置项可以在application.properties
或者 application.yml
中设置,导入文件添加顺序在定义该配置项文件之后。
8. 卷挂载配置树
如果使用spring.config.import
导入附加配置时,该配置项的值没有前缀,就认为导入常规文件或者文件夹,如果以configtree
作为前缀,则告诉Spring Boot该路径为Kubernetes风格的卷加载配置树。
例如,在application.properties
中定义如下配置项:
spring.config.import=configtree:/etc/config
如果有如下挂载内容:
etc/
+- config/
+- my/
| +- application
+- test
那么,最终会在Environment中增加两个属性:my.application
和test
,其值分别为/etc/config/my/application
和/etc/config/test
。
9. 云平台激活
如果只想卷挂载配置树在指定的云平台下才激活,可以使用spring.config.activate.on-cloud-platform
配置。该配置和spring.config.activate.on-profile
相似,只不过使用的是云平台的值,而非profile名称。
例如,上面配置只在Kubernetes下部署才生效,可以如下配置
spring.config.activate.on-cloud-platform=kubernetes
spring.config.import=configtree:/etc/config
10. 第三方配置源
配置项spring.config.import
的值完全是可拔插的,我们可以自定义扩展。可以想象第三方Jar来支持诸如archaius://...
,vault://...
或 zookeeper://...
之类的配置路径。
这部分源码可以参考org.springframework.boot.context.config
包下的ConfigDataLocationResolver
和ConfigDataLoader
类。
11. 仍然使用Spring Boot 2.3配置方式
如果Spring Boot 2.4这种变更让你很不爽,想在Spring Boot 2.4 下使用原有配置文件加载方式, 可以在application.properties
或者 application.yml
文件中将配置项spring.config.use-legacy-processing
置为true
,那么Spring Boot将沿用2.3版本方式处理配置文件。
Spring Boot版本迁移时,一些配置项的变更可能对我们造成困惑,我们可以在pom文件中引入spring-boot-properties-migrator
模块,引入后在项目启动时,当你的配置文件中存在被识别的已经移除的属性时,将会日志打印提示。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope>
</dependency>
版本迁移完成后,再将该模块从pom文件中移除。
参考
- [1] Config file processing in Spring Boot 2.4
- [2] Application properties outside of jar should take precedence over profile-specific variants inside the jar
- [3] Spring Boot Reference Documentation 2.3.6.RELEASE
- [4] Spring Boot Reference Documentation 2.4.1
- [5] Including profiles in spring boot 2.4.0 version
以上是关于Spring Boot v2.4.4源码解析配置文件加载篇一 #2.4版本与2.3版本差异的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot实战源码解析Spring Boot自动配置原理
Spring Boot实战源码解析Spring Boot自动配置原理
SpringBoot 源码解析 ----- Spring Boot的核心能力 - 自动配置源码解析