首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
一. 前言
文章目的 :
- 梳理 Applicaiton 的加载方式
- 梳理 Profile 的处理
二 . 扫描的触发
启动的源头任然是SpringApplication#run , 回顾之前的一篇源码 , 在 SpringApplication 中 ,会执行一段代码 :
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
而一切的起点就是那里 , SpringBoot 通过这一句加载所有的环境信息 :
2.1 SpringApplication # prepareEnvironment
在该环节中 , 对 Environment 进行操作的处理 , 其中包括几个主要的操作 :
- ConfigurableEnvironment 的生成
- configureEnvironment 细粒度处理
- 对 configurationProperties 属性进行处理
- 发布 listener 处理执行不同类型配置文件的处理
- 将 environment 绑定到 SpringApplication
1 | java复制代码C- SpringApplication # prepareEnvironment |
2.1.1 ConfigurableEnvironment 的生成
此处会生成一个 StandardServletEnvironment , 他是 ConfigurableEnvironment 的实现类 , 该对象中存在2个方法 , 分别配置了多个 Source 对象
- customizePropertySources(MutablePropertySources propertySources)
- initPropertySources(ServletContext servletContext,ServletConfig servletConfig)
1 | java复制代码 |
[PRO:0001] 系统属性大概样式 >>>
其中最主要的就是第 4 步 , 该步骤中扫描不同的配置处理类
补充 : getOrCreateEnvironment 的获取
1 | java复制代码private ConfigurableEnvironment getOrCreateEnvironment() { |
2.2 发布 listener 处理执行不同类型配置文件的处理
前面主要是获取 SystemSource , 还没有正式开始 , 我们从 prepareEnvironment 第四步开始看 , 来看看后面怎么处理 :
Step 1 : 发布 Listener , 推动 Enviroment 处理
这里不需要深入太多 , 标准的 Listener 发布方式 , 这里主要发布的是 ApplicationEnvironmentPreparedEvent
1 | java复制代码C- SpringApplicationRunListeners |
Step 2 :ConfigFileApplicationListener 处理该事件
核心的处理方式就是 EnvironmentPostProcessor 的循环 , 后面所有的操作均在其中完成 :
1 | JAVA复制代码private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { |
三 . postProcessors 的处理
前面看了 , 通过调用 postProcessor.postProcessEnvironment 来实现不同的环境加载 , 可以看到 , 此处主要有5个 postProcessors :
PS:0001 ConfigFileApplicationListener 中 postProcessors 有哪些
1 | java复制代码- SystemEnvironmentPropertySourceEnvironmentPostProcessor |
3.1 SystemEnvironmentPropertySourceEnvironmentPostProcessor
该类主要是对 systemEnvironment 进行处理 , 前面看到了 , 实际上前面已经拿到 SystemSource , 此处是对这些配置的二次处理 :
1 | java复制代码public void postProcessEnvironment( |
3.2 SpringApplicationJsonEnvironmentPostProcessor
该类的主要使用形式是在启动 jar 包时使用 , 这种方式的优先级最高:
java -jar xxx.jar --spring.application.json={\"username\":\"ant-black\"}
1 | java复制代码// 从 spring.application.json或等价的SPRING_APPLICATION_JSON中解析JSON , |
3.3 CloudFoundryVcapEnvironmentPostProcessor
一个环境 postprocessor,它知道在现有环境中在哪里找到VCAP(也就是云计算)元数据。它解析VCAP_APPLICATION和VCAP_SERVICES元数据,并将其转储Environment。
这一块比较玄妙 , 首先要知道 VCAP 是什么 :
- IBM Cloud 中有一个概念 : Cloud Foundry 应用程序
- 在 Cloud Foundry 运行的应用程序通过存储在一个名为 VCAP services 的环境变量中的凭证获得对绑定服务实例的访问
- VCAP_APPLICATION 是指云计算的应用单体元数据
1 | java复制代码public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { |
简单来说 , 这个类主要是为了云计算的环境准备的 , 因为没有太多涉及 , 此处就不深入了
3.4 ConfigFileApplicationListener
这一块应该就是最核心的一块了 , 这里会对 Application 文件进行扫描处理 :
Step 1 : ConfigFileApplicationListener 的入口
由于 ConfigFileApplicationListener 本身继承了 EnvironmentPostProcessor , 其本身也会加载配置环境 :
1 | java复制代码// 将配置文件属性源添加到指定的环境 |
Step 2 : 构建 Loader 对象
1 | java复制代码// 来看一看 loader 对象 , 该对象会用于处理属性 Document 并且扫描 |
Step 3 : 扫描所有的路径 , 发起处理
此处通过 getSearchLocations 获得所有的根路径 , 然后依次对根路径下的文件进行扫描
PS :因为处理的顺序不同 , 更靠后的优先级更高
1 | java复制代码private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { |
这里主要有四个默认路径 :
- file:./config/
- file:./
- classpath:/config/
- classpath:/
Step 4 : 对路径进行依次处理
1 | java复制代码private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, |
PS:0002 PropertySourceLoader 的类型
此处提供了 2 种 PropertySourceLoader ,分别是 PropertiesPropertySourceLoader (.properties) 和 YamlPropertySourceLoader (支持 .yml .yaml’)
Step 5 : loadForFileExtension 处理
1 | java复制代码private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, |
Step 6 : 对 Application 文件进行读取
1 | java复制代码private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, |
Step 7 : 调用 loader 流程发起加载
该逻辑就是对Document 的处理了 , 也不用太深入
1 | java复制代码 |
3.5 DebugAgentEnvironmentPostProcessor
作用 : 启用反应堆调试代理
开启 : 调试代理默认是启用的,除非 spring.reactor.debug-agent.enabled 配置属性设置为false
1 | JAVA复制代码// 这是个特殊的类 , 目的是为了快速加载 |
这个类的主要目的就是为了初始化 REACTOR_DEBUGAGENT_CLASS 类 , 而不是为了加载配置 , 这种方式更加快 (学到了!!!!)
四 . 知识点
4.1 Profile 的处理
Step 1 : 当第一步扫描 Document 的时候 , 会从其中获得 profile 属性 , 并且标注从来
Step 2 : 处理 Profiles
起因是上文 Step 6 中调用的 addActiveProfiles 和 addIncludedProfiles
1 | java复制代码void addActiveProfiles(Set<Profile> profiles) { |
到这里还没完 , 还要做相关的处理 :
Step 3: 反复处理 Profiles
回顾上文 Step 5 : loadForFileExtension 处理 中 , 会发现这样一个处理
1 | java复制代码private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, |
没错 , 这里就是把 profile传进去原样再扫描一遍 , 而值就是上一步设置的 value
你以为这就完了吗 ? 这就像盗梦空间里面一样 , 你以为你醒了 ,其实还在一个梦里 , 整个逻辑中涉及多个循环处理 , 而 Profile 的循环开启是在第一层
1 | java复制代码// 当第一遍大循环执行玩抽 , profiles 中就已经有了新的 profiles , 此处会开启第二个大循环 |
补充一下循环体系 :
- Loader # load : while 循环 profile
- ConfigFileApplicationListener # load(1) : foreach location
- ConfigFileApplicationListener # load(1) : foreach names (PS : 这个循环在上个循环的内部)
- ConfigFileApplicationListener # load(2) : for 循环 propertySourceLoaders
- ConfigFileApplicationListener # load(2) : for 循环 FileExtensions
load(1) : load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer)
load(2) : load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer)
4.2 具体的加载顺序
Step 1 : 后缀文件的加载顺序
从 ConfigFileApplicationListener # load 方法种 , 我们可以看到 , 加载顺序依次是 :
- properties
- xml (没错 , 他会试图加载 xml 文件)
- yml
- yaml
总结
本来以为篇幅不会太长, 结构又写了这么多 , 所以将属性的转换放在下一篇将 ,欢迎点赞收藏
修改记录
- V20210612 : 添加 >> 四 . 知识点
- V20210804 : 添加结构图
本文转载自: 掘金