SpringBoot 的配置加载主要通过事件广播机制来完成,即由 SpringApplication
启动过程中,通过调用 SpringApplicationRunListener.environmentPrepared()
触发 ConfigFileApplicationListener
对 ApplicationEvent
事件的响应。
由于本文主要分析配置加载的流程,对于启动过程中的事件传播不做过多的介绍,直接从 ConfigFileApplicationListener
开始学习具体的配置加载流程。
1 |
|
在 ConfigFileApplicationListener
里面,实际上是可以监听两类事件的,其中对 ApplicationEnvironmentPreparedEvent
事件的处理就是我们要学习的重点。该事件处理主要由 EnvironmentPostProcessor
完成的,默认情况下有三种 EnvironmentPostProcessor
实现,其中两种从 META-INF/spring.factories
加载获得,最后一种则是 ConfigFileApplicationListener
本身,也是我们要学习的重点,其实现了 EnvironmentPostProcessor
接口,并对 postProcessEnvironment()
方法进行了重写。
1 |
|
整个流程分为两个步骤:
- 通过内部类
Loader
完成Environment
的加载 - 完成
Environment
与SpringApplication
的绑定
ConfigFileApplicationListener#Loader 实现
1 | public void load() { |
在内部类 Loader
的实现里面,主要包括以下几个步骤:
- 添加
Environment.setActiveProfiles()
指定(或默认)的Profile
到 LIFO 队列,并在队列最后添加一个空元素,以保证默认的配置文件优先加载,如:application.yml
优先application-dev.yml
加载; - 遍历
Profile
队列,依次进行加载,流程分为两个步骤:- 按查询路径、文件名、后缀名等查找配置文件
- 解析配置文件,并将其添加到对应的分组
- 查找配置文件所在路径
- 按配置的查询路径查找配置文件,默认为:
file:./config/
,file:./
,classpath:/config/
,classpath:/
,可通过spring.config.location
进行指定; - 相同路径下,按配置的文件名前缀依次查找配置文件,默认为
application
,可通过spring.config.name
进行调整; - 相同的文件名前缀,则按
PropertySourcesLoader
支持的文件名后缀进行查找,默认为:properties
,xml
,yml
,yaml
,其中properties
,xml
为PropertiesPropertySourceLoader
提供支持,yml
,yaml
由YamlPropertySourceLoader
提供支持;
- 按配置的查询路径查找配置文件,默认为:
- 解析配置文件
- 对于
properties
及xml
文件,直接加载profile对应的文件即可; - 对于
yml
及yaml
文件,由于该类文件语法中支持分块配置,Spring 通过spring.profiles
配置项可将文件分成多个块,加载过程也可以按块加载。未指定 profile 的情况下,直接加载application.yml
的默认块,若指定 profile 为 dev,则按application-dev.yml#default
,application-dev.yml#dev
,application.yml#dev
进行依次加载;
- 对于
- 将解析后所得的
PropertySources
添加到Environment
;
PropertySourcesLoader 实现
配置文件的解析与组装都是由 PropertySourcesLoader
完成的,PropertySourcesLoader
会将加载到的配置信息进行分组,并封装到 EnumerableCompositePropertySource
之中。
1 | public PropertySource<?> load(Resource resource, String group, String name, String profile) throws IOException { |
总结
Environment 加载需要理解文件的查找方式、加载顺序及加载完成后的配置优先级。
查找方式:在文中已经描述的很清楚,按路径、文件名、后缀名依次逐级递进查找。
加载顺序:相对就复杂了很多,以一张图概括如下:
图3.3 Environment加载顺序
这里对 yaml
文件画的比较简单,实际上它与 yml
类似,也是分三步加载的。
- 配置优先级:与加载顺序非常类似,唯一的区别是,组级别的顺序是相反的,先加载的组优先级低于后加载的组,即上图中的 dev 组会覆盖掉 default 组的配置。
最后,再献上一张实验的结果图,与上图的内容一致,任意理解一个即可。
图3.4 Environment加载实验结果