1. 刷新环境Environment的理论分析
在之前我们已经提过bootstrap
配置文件的生效,是直接在BootstrapApplicationListener
中建立一个隔离的bootstrap
容器,从而得到刷新完成的容器的环境对象,并和app容器的环境对象去进行合并。
我们知道bootstrap
配置文件自然是为了实现SpringCloud中很重要的一环,那就是分布式配置中心,我们下面以Nacos为例。
首先我们知道只要启动app
容器,并且里面包含了BootstrapApplicationListener
这个监听器,那么就会启动bootstrap
容器,并将里面的ApplicationContext
的初始化器转移到app
容器当中去,当ApplicationContext
创建好时,就会自动回调ApplicationContext
的初始化器。
而其中一个组件PropertySourceBootstrapConfiguration
,它会自动注入容器中所有的类型的PropertySourceLocator
组件。
在它对ApplicationContext
去进行准备工作的回调方法如下
我们可以看到,它做的事情就是遍历所有的Locator
,去执行它的locateCollection
方法。
我们可以看到,locateCollection
方法其实就是回调locate
方法,去加载属性源(PropertySource,也就是对配置文件的抽象)。
而Nacos
中就实现了这样一个组件NacosPropertySourceLocator
:
我们可以发现,这就是去ConfigServer
当中去加载配置文件的逻辑。
从以上的分析当中,我们可以知道,构建一个app容器,就能得到完整的环境信息。实际上是依赖BootstrapApplicationListener
去构建的隔离容器,最终实现的ConfigServer
中配置文件的加载。
其实在Spring中就是这样去做的,构建一个mini版本的app容器,因为容器就会去回调Locator
,因此就能得到完整的环境信息。再将该容器的环境信息,和真正的app容器去进行合并,就得到了更新之后的环境信息。
2. 在Spring当中的落地实现
自动刷新的入口在RefreshEventListener
这个组件
我们可以看到,它监听了RefreshEvent
事件,当这个事件被发布时,就会回调ContextRefresher
的refresh
方法去完成配置文件的刷新工作。
从上面的代码中,我们可以将其分为三步:
- 1.
updateEnvironment
,更新环境对象,它是一个模板方法,交给子类去进行实现。 - 2.发布
EnvironmentChangeEvent
事件。 - 3.
scope.refreshAll
2.1 更新环境信息
我们先来看第一步(找到子类LegacyContextRefresher
的updateEnvironment
方法)
我们可以看到,其实这就是构建一个mini的app容器,并设置监听器为BootstrapApplicationListener
,在刷新完容器,拿到它执行完成的环境对象,去更新原来的app环境当中的环境信息。
2.2 发布环境改变事件并进行rebind
接着来看第二步,发布EnvironmentChangeEvent
事件,其中一个组件ConfigurationPropertiesRebinder
,就监听了这个事件。
我们可以看到,当环境发生改变的事件发布之后,它就会遍历所有的标有@ConfigurationProperties
注解的Bean
去进行rebind
,下面我们来看rebind
做了什么?
我们可以看到,首先是摧毁Bean
,然后就是对Bean
去进行重新初始化(重新初始化的过程中,就会有处理@ConfigurationProperties
注解的Bean去进行值的设置)
我们来看ConfigurationPropertiesBeans
这个组件是如何收集@ConfigurationProperties
的组件的?
我们可以看到,它是一个BeanPostProcessor
,可以在Bean
初始化前后去进行干预,它首先把RefreshScope
的Bean
给排除掉,再去扫描@ConfigruationProperties
注解,也就是说,它维护的是非RefreshScope
,并且标注了@ConfigurationProperties
注解的组件。
2.3 刷新RefreshScope作用域内的Bean
image.png image.png我们可以看到,它先将自己的作用域的Bean
去进行destory
(将自己这个作用域内的Bean的缓存直接清空,并完成destroy
回调),然后发布RefreshScopeRefreshedEvent
事件。
当RefreshScope
这个作用域被刷新时,代表下次获取RefreshScope
内的Bean
时,就得重新调用createBean
方法去创建一个Bean
,重新创建,自然是会拿到新的配置信息,能实现刷新效果,没毛病。
如何让自己成为一个RefreshScope
的Bean
呢?给一个Bean
上标上@RefreshScope
注解即可。
2.4 用到的相关组件从何而来?
在cloud-context
包的spring.factories
当中配置了下面这个配置类,会给容器中导入ConfigurationPropertiesBeans
和ConfigurationPropertiesRebinder
组件,去支持@ConfigurationProperties
注解的Bean
的rebind
。
2.5 Nacos
如何整合Spring去进行发布事件?
Nacos
通过spring.factories
给容器中导入了NacosConfigAutoConfiguration
这个组件,这个组件内部给容器中导入了一堆组件。
其中我们需要关注的组件是NacosContextRefresher
这个组件它也是一个监听器,负责监听容器已经启动完成的事件,当容器启动完成的事件发布之后,它就会将已经注册的全部属性源的dataId
配合group
,注册一个监听器到Nacos
的ConfigService
当中去,而监听器内部做的事就是发布RefreshEvent
事件。
当Nacos
的ConfigServer
中的配置信息发生了改变,就会推送消息给NacosClient
,而此时NacosClient
就会回调相应的监听器,回调监听器时,自然就肯定会发布RefreshEvent
事件。
当RefreshEvent
事件到来时,就会触发我们上面提到的刷新工作,将@ConfigurationProperties
注解的Bean
和@RefreshScope的
Bean`去完成刷新,完成刷新工作之后,下次我们再访问相应的对象时,就会拿到新的对象,获取到新的属性值,从而实现配置文件的自动。
网友评论