1. 刷新環(huán)境Environment的理論分析
在之前我們已經(jīng)提過bootstrap
配置文件的生效荣挨,是直接在BootstrapApplicationListener
中建立一個隔離的bootstrap
容器琴锭,從而得到刷新完成的容器的環(huán)境對象,并和app容器的環(huán)境對象去進行合并丁逝。
我們知道bootstrap
配置文件自然是為了實現(xiàn)SpringCloud中很重要的一環(huán)惊搏,那就是分布式配置中心,我們下面以Nacos為例仪搔。
首先我們知道只要啟動app
容器瘾婿,并且里面包含了BootstrapApplicationListener
這個監(jiān)聽器,那么就會啟動bootstrap
容器烤咧,并將里面的ApplicationContext
的初始化器轉移到app
容器當中去偏陪,當ApplicationContext
創(chuàng)建好時,就會自動回調ApplicationContext
的初始化器煮嫌。
而其中一個組件PropertySourceBootstrapConfiguration
笛谦,它會自動注入容器中所有的類型的PropertySourceLocator
組件。
在它對ApplicationContext
去進行準備工作的回調方法如下
我們可以看到昌阿,它做的事情就是遍歷所有的Locator
饥脑,去執(zhí)行它的locateCollection
方法恳邀。
我們可以看到,locateCollection
方法其實就是回調locate
方法灶轰,去加載屬性源(PropertySource谣沸,也就是對配置文件的抽象)。
而Nacos
中就實現(xiàn)了這樣一個組件NacosPropertySourceLocator
:
我們可以發(fā)現(xiàn)笋颤,這就是去ConfigServer
當中去加載配置文件的邏輯乳附。
從以上的分析當中,我們可以知道椰弊,構建一個app容器许溅,就能得到完整的環(huán)境信息。實際上是依賴BootstrapApplicationListener
去構建的隔離容器秉版,最終實現(xiàn)的ConfigServer
中配置文件的加載贤重。
其實在Spring中就是這樣去做的,構建一個mini版本的app容器清焕,因為容器就會去回調Locator
并蝗,因此就能得到完整的環(huán)境信息。再將該容器的環(huán)境信息秸妥,和真正的app容器去進行合并滚停,就得到了更新之后的環(huán)境信息。
2. 在Spring當中的落地實現(xiàn)
自動刷新的入口在RefreshEventListener
這個組件
我們可以看到粥惧,它監(jiān)聽了RefreshEvent
事件键畴,當這個事件被發(fā)布時,就會回調ContextRefresher
的refresh
方法去完成配置文件的刷新工作突雪。
從上面的代碼中起惕,我們可以將其分為三步:
- 1.
updateEnvironment
,更新環(huán)境對象咏删,它是一個模板方法惹想,交給子類去進行實現(xiàn)。 - 2.發(fā)布
EnvironmentChangeEvent
事件督函。 - 3.
scope.refreshAll
2.1 更新環(huán)境信息
我們先來看第一步(找到子類LegacyContextRefresher
的updateEnvironment
方法)
我們可以看到嘀粱,其實這就是構建一個mini的app容器,并設置監(jiān)聽器為BootstrapApplicationListener
辰狡,在刷新完容器锋叨,拿到它執(zhí)行完成的環(huán)境對象,去更新原來的app環(huán)境當中的環(huán)境信息宛篇。
2.2 發(fā)布環(huán)境改變事件并進行rebind
接著來看第二步悲柱,發(fā)布EnvironmentChangeEvent
事件,其中一個組件ConfigurationPropertiesRebinder
些己,就監(jiān)聽了這個事件豌鸡。
我們可以看到嘿般,當環(huán)境發(fā)生改變的事件發(fā)布之后,它就會遍歷所有的標有@ConfigurationProperties
注解的Bean
去進行rebind
涯冠,下面我們來看rebind
做了什么炉奴?
我們可以看到,首先是摧毀Bean
蛇更,然后就是對Bean
去進行重新初始化(重新初始化的過程中瞻赶,就會有處理@ConfigurationProperties
注解的Bean去進行值的設置)
我們來看ConfigurationPropertiesBeans
這個組件是如何收集@ConfigurationProperties
的組件的?
我們可以看到派任,它是一個BeanPostProcessor
砸逊,可以在Bean
初始化前后去進行干預,它首先把RefreshScope
的Bean
給排除掉掌逛,再去掃描@ConfigruationProperties
注解师逸,也就是說,它維護的是非RefreshScope
豆混,并且標注了@ConfigurationProperties
注解的組件篓像。
2.3 刷新RefreshScope作用域內的Bean
我們可以看到,它先將自己的作用域的Bean
去進行destory
(將自己這個作用域內的Bean的緩存直接清空皿伺,并完成destroy
回調)员辩,然后發(fā)布RefreshScopeRefreshedEvent
事件。
當RefreshScope
這個作用域被刷新時鸵鸥,代表下次獲取RefreshScope
內的Bean
時奠滑,就得重新調用createBean
方法去創(chuàng)建一個Bean
,重新創(chuàng)建妒穴,自然是會拿到新的配置信息养叛,能實現(xiàn)刷新效果,沒毛病宰翅。
如何讓自己成為一個RefreshScope
的Bean
呢?給一個Bean
上標上@RefreshScope
注解即可爽室。
2.4 用到的相關組件從何而來汁讼?
在cloud-context
包的spring.factories
當中配置了下面這個配置類,會給容器中導入ConfigurationPropertiesBeans
和ConfigurationPropertiesRebinder
組件阔墩,去支持@ConfigurationProperties
注解的Bean
的rebind
嘿架。
2.5 Nacos
如何整合Spring去進行發(fā)布事件?
Nacos
通過spring.factories
給容器中導入了NacosConfigAutoConfiguration
這個組件啸箫,這個組件內部給容器中導入了一堆組件耸彪。
其中我們需要關注的組件是NacosContextRefresher
這個組件它也是一個監(jiān)聽器,負責監(jiān)聽容器已經(jīng)啟動完成的事件忘苛,當容器啟動完成的事件發(fā)布之后蝉娜,它就會將已經(jīng)注冊的全部屬性源的dataId
配合group
唱较,注冊一個監(jiān)聽器到Nacos
的ConfigService
當中去,而監(jiān)聽器內部做的事就是發(fā)布RefreshEvent
事件召川。
當Nacos
的ConfigServer
中的配置信息發(fā)生了改變南缓,就會推送消息給NacosClient
,而此時NacosClient
就會回調相應的監(jiān)聽器荧呐,回調監(jiān)聽器時汉形,自然就肯定會發(fā)布RefreshEvent
事件。
當RefreshEvent
事件到來時倍阐,就會觸發(fā)我們上面提到的刷新工作概疆,將@ConfigurationProperties
注解的Bean
和@RefreshScope的
Bean`去完成刷新,完成刷新工作之后峰搪,下次我們再訪問相應的對象時岔冀,就會拿到新的對象,獲取到新的屬性值罢艾,從而實現(xiàn)配置文件的自動楣颠。