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日记