8000字+22张图探秘SpringCloud配置中心的核心原理
大家好,我是三友~~
这篇文章来扒一扒SpringCloud配置中心的核心原理。
不知你是否跟我一样,在刚开始使用SpringCloud配置中心的时候也有很多的疑惑:
- SpringCloud是什么时候去拉取配置中心的?
- 配置中心客户端的配置信息为什么要写在bootstrap文件中?
- 对象中注入的属性是如何动态刷新的?
- 一些开源的配置中心是如何整合SpringCloud的?
- ...
本文就通过探讨上述问题来探秘SpringCloud配置中心核心的底层原理。
从SpringBoot的启动过程说起
在SpringBoot启动的时候会经历一系列步骤,核心就是SpringApplication的run方法的逻辑

整个过程大致可以划分为三个阶段:

ApplicationContext刷新前阶段,这个阶段主要也干三件事
- 准备Environment(注意我这里加粗了,你懂得),也就是准备SpringBoot的整个外部化配置的对象
- 创建一个ApplicationContext
- 为ApplicationContext做一些准备工作

ApplicationContext刷新阶段,这个阶段其实就是调用ApplicationContext#refresh方法来刷新容器

刷新的整个过程可以看我之前写的万字+20张图剖析Spring启动时12个核心步骤这篇文章
ApplicationContext刷新后阶段,这个阶段其实就是收尾的阶段,这个过程其实没有什么非常核心的事
ok,在说完上面这三个阶段之后,思考一个问题
你觉得在上面的三个阶段,哪个阶段最有可能从配置中心拉取配置?
其实稍微思考一下,肯定是想到的就是刷新前阶段
因为我已经明示了,准备Environment

玩笑归玩笑,为什么是这个阶段?
很好理解,因为这个阶段是准备Environment,也就是准备外部化配置
只需要在这个阶段加载配置中心的配置,放到Environment中,后面在整个ApplicationContext刷新阶段创建Bean的时候,就可以使用到配置中心的配置了
其实不光是配置中心的配置,比如配置文件的配置,也是在这里阶段读取的
至于如何实现的,我们接着往下瞅
准备Environment的核心操作
上一节得出一个结论
准备Environment,也就是prepareEnvironment方法的实现,是拉取配置的核心

不过在说这个方法之前,先来讲一下一些前置操作
前置操作
在SpringApplication创建的时候,会去加载spring.factories中的一些对象,其中就包括:
-
org.springframework.context.ApplicationListener键对应的ApplicationListener的实现

-
org.springframework.boot.SpringApplicationRunListener键对应的SpringApplicationRunListener的实现类

SpringApplicationRunListener仅仅只有一个实现EventPublishingRunListener

构造的时候会创建一个SimpleApplicationEventMulticaster,再将加载的ApplicationListener添加进去
SimpleApplicationEventMulticaster是用来发布事件用的,不清楚的话可以看三万字盘点Spring 9大核心基础功能这篇文章
按照传统,画张图来理一下这部分前置操作

prepareEnvironment的核心逻辑
接着来讲一下prepareEnvironment方法

这个方法会首先创建一个Environment对象
之后会执行这么一行方法,传入刚刚创建的Environment对象
listeners.environmentPrepared(environment);
这个方法最终会走到这个方法
EventPublishingRunListener#environmentPrepared

这个方法最终会发布一个ApplicationEnvironmentPreparedEvent事件
而对这个事件有两个特别重要的监听器:
- ConfigFileApplicationListener
- BootstrapApplicationListener
这些监听器都是通过前置操作从spring.factories配置文件中加载的
ConfigFileApplicationListener,用来处理配置文件的,他会解析配置文件的配置,放到Environment中
BootstrapApplicationListener这个跟本文探讨的主题相关了,它是用来专门来跟配置中心交互的
到这,我们就找到了SpringCloud配置中心配置拉取的整个入口逻辑
不过在分析BootstrapApplicationListener是如何从配置中心拉取配置的之前,先来张图总结一下这部分prepareEnvironment的操作

SpringCloud是如何巧妙地拉取配置的?
在BootstrapApplicationListener中,他首先也会创建一个SpringApplication去执行

其实本质上就是创建一个Spring容器,也就是ApplicationContext
这个容器非常重要,这个容器是专门用来跟配置中心交互的
这个容器在创建的时候会给它两个比较重要的配置
第一个就是设置这个容器所用的配置文件的名称

默认就是bootstrap
这就解释了为什么配置中心的配置信息需要写在bootstrap配置文件中
第二个就是会加入一个配置类
BootstrapImportSelectorConfiguration

这个配置类又会通过@Import注解导入另一个配置类
BootstrapImportSelector

BootstrapImportSelector实现了(间接)ImportSelector接口
那么这个容器在启动的时候,就会调用BootstrapImportSelector的selectImports方法的实现获取到一些配置类
而BootstrapImportSelector的selectImports实现从截图中也就可以看出
他会加载所有的spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration的配置类
其实这里
@BootstrapConfiguration的作用其实跟@EnableAutoConfiguration的作用是差不多的,都是用来导入配置类的
所以,总的来说,这个用来跟配置中心交互的Spring容器最最主要就是干两件事:
- 加载bootstrap配置文件
- 加载所有的spring.factories中的键为
org.springframework.cloud.bootstrap.BootstrapConfiguration对应的配置类

而在spring-cloud-context包下,@BootstrapConfiguration会导入一个很重要的配置类

PropertySourceBootstrapConfiguration

这个配置类中会注入这么一个集合对象
PropertySourceLocator

这个接口非常非常重要,先来看看注释
Strategy for locating (possibly remote) property sources for the Environment. Implementations should not fail unless they intend to prevent the application from starting.
我用我的四级英语功力给大家翻译一下
以一种策略的方式为Environment定位(可能是远程)属性配置(PropertySource)。实现不应该失败,除非打算阻止应用程序启动。
从这个翻译后的意思就是说,这个接口是用来定位,也就是说获取属性配置的
并且可能是远程告诉我们一个很重要的信息,那就是获取的配置信息不仅仅可以存在本地,而且还可以存在远程。
远程?作者这里就差直接告诉你可以从配置中心获取了。。
所以这个接口的作用就是用配置中心获取配置的!
那么自然而然不同的配置中心要想整合到SpringCloud就得实现这个接口
当注入完PropertySourceLocator集合之后,在某个阶段会调用所有的PropertySourceLocator,获取配置中心中的配置

之后在把这些配置放到Environment中
这样在ApplicationContext的刷新阶段就可以使用到配置中心的那些配置了
小总结
到这我们就弄明白了在项目启动中加载配置中心的配置了
其实就是项目在启动时会额外创建一个跟配置中心相关的Spring容器
这个容器会去加载bootstrap配置文件和所有的spring.factories中的键为org.springframework.cloud.bootstrap.BootstrapConfiguration对应的配置类
之后会去调用这个容器中所有的PropertySourceLocator对象,从配置中心获取配置
再放到Environment中就完成了启动时从配置中心获取配置的方式
最后,来张全家福概括一下前面整体的步骤

文章转载自公众号:三友的java日记




















