版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
關(guān)于SpringCloud配置,你了解多少?需求不知不覺(jué),web開發(fā)已經(jīng)進(jìn)入“微服務(wù)”、”分布式”的時(shí)代,致力于提供通用Java開發(fā)解決方案的Spring自然不甘人后,提出了SpringCloud來(lái)擴(kuò)大Spring在微服務(wù)方面的影響,也取得了市場(chǎng)的認(rèn)可,在我們的業(yè)務(wù)中也有應(yīng)用。前些天,我在一個(gè)需求中也遇到了springcloud的相關(guān)問(wèn)題。我們?cè)谟玫氖荢pringCloud的config模塊,它是用來(lái)支持分布式配置的,原來(lái)單機(jī)配置在使用了SpringCloud之后,可以支持第三方存儲(chǔ)配置和配置的動(dòng)態(tài)修改和重新加載,自己在業(yè)務(wù)代碼里實(shí)現(xiàn)配置的重新加載,SpringCloud將整個(gè)流程抽離為框架,并很好的融入到Spring原有的配置和Bean模塊內(nèi)。雖然在解決需求問(wèn)題時(shí)走了些彎路,但也借此機(jī)會(huì)了解了SpringCloud的一部分,抽空總結(jié)一下問(wèn)題和在查詢問(wèn)題中了解到的知識(shí),分享出來(lái)讓再遇到此問(wèn)題的同學(xué)少踩坑吧。本文基于Spring5.0.5、SpringBoot2.0.1和SpringCloud2.0.2。背景和問(wèn)題我們的服務(wù)原來(lái)有一批單機(jī)的配置,由于同一key的配置太長(zhǎng),于是將其配置為數(shù)組的形式,并使用SpringBoot的
@ConfigurationProperties
和
@Value
注解來(lái)解析為Bean屬性。搜索公眾號(hào)后端架構(gòu)師回復(fù)關(guān)鍵字“架構(gòu)整潔”,獲取一份驚喜禮包。properties文件配置像:test.config.elements[0]=value1
test.config.elements[1]=value2
test.config.elements[2]=value3在使用時(shí):@ConfigurationProperties(prefix="test.config")
Class
Test{
@Value("${#elements}")
private
String[]
elements;
}這樣,Spring會(huì)對(duì)Test類自動(dòng)注入,將數(shù)組[value1,value2,value3]注入到elements屬性內(nèi)。而我們使用SpringCloud自動(dòng)加載配置的姿勢(shì)是這樣:@RefreshScope
class
Test{
@Value("${test.config.elements}")
private
String[]
elements;
}使用
@RefreshScope
注解的類,在環(huán)境變量有變動(dòng)后會(huì)自動(dòng)重新加載,將最新的屬性注入到類屬性內(nèi),但它卻不支持?jǐn)?shù)組的自動(dòng)注入。而我的目標(biāo)是能找到一種方式,使其即支持注入數(shù)組類型的屬性,又能使用SpringCloud的自動(dòng)刷新配置的特性。環(huán)境和屬性無(wú)論SpringCloud的特性如何優(yōu)秀,在Spring的地盤,還是要入鄉(xiāng)隨俗,和Spring的基礎(chǔ)組件打成一片。所以為了了解整個(gè)流程,我們就要先了解Spring的基礎(chǔ)。Spring是一個(gè)大容器,它不光存儲(chǔ)Bean和其中的依賴,還存儲(chǔ)著整個(gè)應(yīng)用內(nèi)的配置,相對(duì)于BeanFactory存儲(chǔ)著各種Bean,Spring管理環(huán)境配置的容器就是
Environment,從Environment內(nèi),我們能根據(jù)key獲取所有配置,還能根據(jù)不同的場(chǎng)景(Profile,如dev,test,prod)來(lái)切換配置。但Spring管理配置的最小單位并不是屬性,而是
PropertySource
(屬性源),我們可以理解PropertySource是一個(gè)文件,或是某張配置數(shù)據(jù)表,Spring在Environment內(nèi)維護(hù)一個(gè)PropertySourceList,當(dāng)我們獲取配置時(shí),Spring從這些PropertySource內(nèi)查找到對(duì)應(yīng)的值,并使用
ConversionService
將值轉(zhuǎn)換為對(duì)應(yīng)的類型返回。SpringCloud配置刷新機(jī)制分布式配置SpringCloud內(nèi)提供了
PropertySourceLocator
接口來(lái)對(duì)接Spring的PropertySource體系,通過(guò)PropertySourceLocator,我們就拿到一個(gè)”自定義”的PropertySource,SpringCloud里還有一個(gè)實(shí)現(xiàn)
ConfigServicePropertySourceLocator,通過(guò)它,我們可以定義一個(gè)遠(yuǎn)程的ConfigService,通過(guò)公用這個(gè)ConfigService來(lái)實(shí)現(xiàn)分布式的配置服務(wù)。從
ConfigClientProperties
這個(gè)配置類我們可以看得出來(lái),它也為遠(yuǎn)程配置預(yù)設(shè)了用戶名密碼等安全控制選項(xiàng),還有l(wèi)abel用來(lái)區(qū)分服務(wù)池等配置。scope配置刷新遠(yuǎn)程配置有了,接下來(lái)就是對(duì)變化的監(jiān)測(cè)和基于配置變化的刷新。SpringCloud提供了
ContextRefresher
來(lái)幫助我們實(shí)現(xiàn)環(huán)境的刷新,其主要邏輯在
refreshEnvironment
方法和
scope.refreshAll()
方法,我們分開來(lái)看。我們先來(lái)看springcloud支持的scope.refreshAll方法。public
void
refreshAll()
{
super.destroy();
this.context.publishEvent(new
RefreshScopeRefreshedEvent());
}scope.refreshAll則更”野蠻”一些,直接銷毀了scope,并發(fā)布了一個(gè)RefreshScopeRefreshedEvent事件,scope的銷毀會(huì)導(dǎo)致scope內(nèi)(被RefreshScope注解)所有的bean都會(huì)被銷毀。而這些被強(qiáng)制設(shè)置為lazyInit的bean再次創(chuàng)建時(shí),也就完成了新配置的重新加載。ConfigurationProperties配置刷新然后再回過(guò)頭來(lái)看refreshEnvironment方法。Map
before
=
extract(this.context.getEnvironment().getPropertySources());
addConfigFilesToEnvironment();
Set
keys
=
changes(before,extract(this.context.getEnvironment().getPropertySources())).keySet();
this.context.publishEvent(new
EnvironmentChangeEvent(context,
keys));
return
keys;它讀取了環(huán)境內(nèi)所有PropertySource內(nèi)的配置后,重新創(chuàng)建了一個(gè)SpringApplication以刷新配置,再次讀取所有配置項(xiàng)并得到與前面保存的配置項(xiàng)的對(duì)比,最后將前后配置差發(fā)布了一個(gè)
EnvironmentChangeEvent
事件。而EnvironmentChangeEvent的監(jiān)聽器是由ConfigurationPropertiesRebinder實(shí)現(xiàn)的,其主要邏輯在
rebind
方法。搜索公眾號(hào)后端架構(gòu)師回復(fù)關(guān)鍵字“面試”,獲取一份驚喜禮包。Object
bean
=
this.applicationContext.getBean(name);
if
(AopUtils.isAopProxy(bean))
{
bean
=
ProxyUtils.getTargetObject(bean);
}
if
(bean
!=
null)
{
this.applicationContext.getAutowireCapableBeanFactory().destroyBean(bean);
this.applicationContext.getAutowireCapableBeanFactory().initializeBean(bean,
name);
return
true;可以看到它的處理邏輯,就是把其內(nèi)部存儲(chǔ)的
ConfigurationPropertiesBeans
依次執(zhí)行銷毀邏輯,再執(zhí)行初始化邏輯實(shí)現(xiàn)屬性的重新綁定。這里可以知道,SpringCloud在進(jìn)行配置刷新時(shí)是考慮過(guò)ConfigurationProperties的,經(jīng)過(guò)測(cè)試,在ContextRefresher刷新上下文后,ConfigurationProperties注解類的屬性是會(huì)進(jìn)行動(dòng)態(tài)刷新的。測(cè)試一次就解決的事情,感覺(jué)有些白忙活了。。不過(guò)既然查到這里了,就再往下深入一些。Bean的創(chuàng)建與環(huán)境接著我們?cè)賮?lái)看一下,環(huán)境里的屬性都是怎么在
Bean創(chuàng)建時(shí)被使用的。我們知道,Spring的Bean都是在BeanFactory內(nèi)創(chuàng)建的,創(chuàng)建邏輯的入口在
AbstractBeanFactory.doGetBean(name,requiredType,args,false)
方法,而具體實(shí)現(xiàn)在
AbstractAutowireCapableBeanFactory.doCreateBean
方法內(nèi),在這個(gè)方法里,實(shí)現(xiàn)了Bean實(shí)例的創(chuàng)建、屬性填充、初始化方法調(diào)用等邏輯。在這里,有一個(gè)非常復(fù)雜的步驟就是調(diào)用全局的
BeanPostProcessor,這個(gè)接口是Spring為Bean創(chuàng)建準(zhǔn)備的勾子接口,實(shí)現(xiàn)這個(gè)接口的類可以對(duì)Bean創(chuàng)建時(shí)的操作進(jìn)行修改。它是一個(gè)非常重要的接口,是我們能干涉SpringBean創(chuàng)建流程的重要入口。我們要說(shuō)的是它的一種具體實(shí)現(xiàn)
ConfigurationPropertiesBindingPostProcessor,它通過(guò)調(diào)用鏈
ConfigurationPropertiesBinder.bind()-->Binder.bindObject()-->Binder.findProperty()
方法查找環(huán)境內(nèi)的屬性。private
ConfigurationProperty
findProperty(ConfigurationPropertyName
name,
Context
context)
{
if
(name.isEmpty())
{
return
null;
}
return
context.streamSources()
.map((source)
->
source.getConfigurationProperty(name))
.filter(Objects::nonNull).findFirst().orElse(null);
}找到對(duì)應(yīng)的屬性后,再使用converter將屬性轉(zhuǎn)換為對(duì)應(yīng)的類型注入到Bean骨。private
Object
bindProperty(Bindable
target,
Context
context,
ConfigurationProperty
property)
{
context.setConfigurationProperty(property);
Object
result
=
property.getValue();
result
=
this.placeholdersResolver.resolvePlaceholders(result);
result
=
context.getConverter().convert(result,
target);
return
result;
}一種trick方式由上面可以看到,Spring是支持@ConfigurationProperties屬性的動(dòng)態(tài)修改的,但在查詢流程時(shí),我也找到了一種比較trick的方式。我們先來(lái)整理動(dòng)態(tài)屬性注入的關(guān)鍵點(diǎn),再?gòu)倪@些關(guān)鍵點(diǎn)里找可修改點(diǎn)。PropertySourceLocator將PropertySource從遠(yuǎn)程數(shù)據(jù)源引入,如果這時(shí)我們能修改數(shù)據(jù)源的結(jié)果就能達(dá)到目的,可是SpringCloud的遠(yuǎn)程資源定位器ConfigServicePropertySourceLocator和遠(yuǎn)程調(diào)用工具RestTemplate都是實(shí)現(xiàn)類,如果生硬地對(duì)其繼承并修改,代碼很不優(yōu)雅。Bean創(chuàng)建時(shí)會(huì)依次使用BeanPostProcessor對(duì)上下文進(jìn)行操作。這時(shí)添加一個(gè)BeanPostProcessor,可以手動(dòng)實(shí)現(xiàn)對(duì)Bean屬性的修改。但這種方式實(shí)現(xiàn)起來(lái)很復(fù)雜,而且由于每一個(gè)BeanPostProcessor在所有Bean創(chuàng)建時(shí)都會(huì)調(diào)用,可能會(huì)有安全問(wèn)題。Spring會(huì)在解決類屬性注入時(shí),使用PropertyResolver將配置項(xiàng)解析為類屬性指定的類型。這時(shí)候添加屬性解析器PropertyResolver或類型轉(zhuǎn)換器ConversionService可以插手屬性的操作。但它們都只負(fù)責(zé)處理一個(gè)屬性,由于我的目標(biāo)是”多個(gè)”屬性變成一個(gè)屬性,它們也無(wú)能為力。我這里能想到的方式是借用Spring自動(dòng)注入的能力,把EnvironmentBean注入到某個(gè)類中,然后在類的初始化方法里對(duì)Environment內(nèi)的PropertySource里進(jìn)行修改,也可以達(dá)成目的,這里貼一下偽代碼。@Component
@RefreshScope
//
借用
Spring
Cloud
實(shí)現(xiàn)此
Bean
的刷新
public
class
ListSupportPropertyResolver
{
@Autowired
ConfigurableEnvironment
env;
//
將環(huán)境注入到
Bean
內(nèi)是修改環(huán)境的重要前提
@PostConstruct
public
void
init()
{
//
將屬性鍵值對(duì)從環(huán)境內(nèi)取出
Map
properties
=
extract(e
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 輪胎買賣合同
- 2025年度綠色環(huán)保電梯安裝與保養(yǎng)一體化合同4篇
- 2025年度個(gè)人科技研發(fā)貸款循環(huán)合同模板2篇
- 2025年度某局新型城鎮(zhèn)化勞務(wù)分包結(jié)算執(zhí)行標(biāo)準(zhǔn)合同4篇
- 2025版民間借貸擔(dān)保方式變革與合同范本解讀4篇
- 2025年度智能家電出口購(gòu)銷合同范本4篇
- 二零二五年度智慧醫(yī)療健康平臺(tái)建設(shè)項(xiàng)目承包合同簽約與遠(yuǎn)程醫(yī)療服務(wù)協(xié)議(2024版)3篇
- 二零二五年度紙箱包裝質(zhì)量監(jiān)控承包合同4篇
- 二零二五年度個(gè)性化定制汽車質(zhì)押借款合同樣本4篇
- 二零二五年度水電工程安裝與運(yùn)營(yíng)維護(hù)服務(wù)合同范本4篇
- 數(shù)學(xué)-山東省2025年1月濟(jì)南市高三期末學(xué)習(xí)質(zhì)量檢測(cè)濟(jì)南期末試題和答案
- 中儲(chǔ)糧黑龍江分公司社招2025年學(xué)習(xí)資料
- 湖南省長(zhǎng)沙市2024-2025學(xué)年高一數(shù)學(xué)上學(xué)期期末考試試卷
- 船舶行業(yè)維修保養(yǎng)合同
- 2024年林地使用權(quán)轉(zhuǎn)讓協(xié)議書
- 物流有限公司安全生產(chǎn)專項(xiàng)整治三年行動(dòng)實(shí)施方案全國(guó)安全生產(chǎn)專項(xiàng)整治三年行動(dòng)計(jì)劃
- 2025屆江蘇省13市高三最后一卷生物試卷含解析
- 當(dāng)前中國(guó)個(gè)人極端暴力犯罪個(gè)案研究
- 中國(guó)象棋比賽規(guī)則
- 7天減肥餐食譜給你最能瘦的一周減肥食譜
- GB/T 31525-2015圖形標(biāo)志電動(dòng)汽車充換電設(shè)施標(biāo)志
評(píng)論
0/150
提交評(píng)論