版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
關(guān)于SpringCloud配置,你了解多少?需求不知不覺,web開發(fā)已經(jīng)進入“微服務(wù)”、”分布式”的時代,致力于提供通用Java開發(fā)解決方案的Spring自然不甘人后,提出了SpringCloud來擴大Spring在微服務(wù)方面的影響,也取得了市場的認可,在我們的業(yè)務(wù)中也有應(yīng)用。前些天,我在一個需求中也遇到了springcloud的相關(guān)問題。我們在用的是SpringCloud的config模塊,它是用來支持分布式配置的,原來單機配置在使用了SpringCloud之后,可以支持第三方存儲配置和配置的動態(tài)修改和重新加載,自己在業(yè)務(wù)代碼里實現(xiàn)配置的重新加載,SpringCloud將整個流程抽離為框架,并很好的融入到Spring原有的配置和Bean模塊內(nèi)。雖然在解決需求問題時走了些彎路,但也借此機會了解了SpringCloud的一部分,抽空總結(jié)一下問題和在查詢問題中了解到的知識,分享出來讓再遇到此問題的同學(xué)少踩坑吧。本文基于Spring5.0.5、SpringBoot2.0.1和SpringCloud2.0.2。背景和問題我們的服務(wù)原來有一批單機的配置,由于同一key的配置太長,于是將其配置為數(shù)組的形式,并使用SpringBoot的
@ConfigurationProperties
和
@Value
注解來解析為Bean屬性。搜索公眾號后端架構(gòu)師回復(fù)關(guān)鍵字“架構(gòu)整潔”,獲取一份驚喜禮包。properties文件配置像:test.config.elements[0]=value1
test.config.elements[1]=value2
test.config.elements[2]=value3在使用時:@ConfigurationProperties(prefix="test.config")
Class
Test{
@Value("${#elements}")
private
String[]
elements;
}這樣,Spring會對Test類自動注入,將數(shù)組[value1,value2,value3]注入到elements屬性內(nèi)。而我們使用SpringCloud自動加載配置的姿勢是這樣:@RefreshScope
class
Test{
@Value("${test.config.elements}")
private
String[]
elements;
}使用
@RefreshScope
注解的類,在環(huán)境變量有變動后會自動重新加載,將最新的屬性注入到類屬性內(nèi),但它卻不支持數(shù)組的自動注入。而我的目標是能找到一種方式,使其即支持注入數(shù)組類型的屬性,又能使用SpringCloud的自動刷新配置的特性。環(huán)境和屬性無論SpringCloud的特性如何優(yōu)秀,在Spring的地盤,還是要入鄉(xiāng)隨俗,和Spring的基礎(chǔ)組件打成一片。所以為了了解整個流程,我們就要先了解Spring的基礎(chǔ)。Spring是一個大容器,它不光存儲Bean和其中的依賴,還存儲著整個應(yīng)用內(nèi)的配置,相對于BeanFactory存儲著各種Bean,Spring管理環(huán)境配置的容器就是
Environment,從Environment內(nèi),我們能根據(jù)key獲取所有配置,還能根據(jù)不同的場景(Profile,如dev,test,prod)來切換配置。但Spring管理配置的最小單位并不是屬性,而是
PropertySource
(屬性源),我們可以理解PropertySource是一個文件,或是某張配置數(shù)據(jù)表,Spring在Environment內(nèi)維護一個PropertySourceList,當(dāng)我們獲取配置時,Spring從這些PropertySource內(nèi)查找到對應(yīng)的值,并使用
ConversionService
將值轉(zhuǎn)換為對應(yīng)的類型返回。SpringCloud配置刷新機制分布式配置SpringCloud內(nèi)提供了
PropertySourceLocator
接口來對接Spring的PropertySource體系,通過PropertySourceLocator,我們就拿到一個”自定義”的PropertySource,SpringCloud里還有一個實現(xiàn)
ConfigServicePropertySourceLocator,通過它,我們可以定義一個遠程的ConfigService,通過公用這個ConfigService來實現(xiàn)分布式的配置服務(wù)。從
ConfigClientProperties
這個配置類我們可以看得出來,它也為遠程配置預(yù)設(shè)了用戶名密碼等安全控制選項,還有l(wèi)abel用來區(qū)分服務(wù)池等配置。scope配置刷新遠程配置有了,接下來就是對變化的監(jiān)測和基于配置變化的刷新。SpringCloud提供了
ContextRefresher
來幫助我們實現(xiàn)環(huán)境的刷新,其主要邏輯在
refreshEnvironment
方法和
scope.refreshAll()
方法,我們分開來看。我們先來看springcloud支持的scope.refreshAll方法。public
void
refreshAll()
{
super.destroy();
this.context.publishEvent(new
RefreshScopeRefreshedEvent());
}scope.refreshAll則更”野蠻”一些,直接銷毀了scope,并發(fā)布了一個RefreshScopeRefreshedEvent事件,scope的銷毀會導(dǎo)致scope內(nèi)(被RefreshScope注解)所有的bean都會被銷毀。而這些被強制設(shè)置為lazyInit的bean再次創(chuàng)建時,也就完成了新配置的重新加載。ConfigurationProperties配置刷新然后再回過頭來看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)建了一個SpringApplication以刷新配置,再次讀取所有配置項并得到與前面保存的配置項的對比,最后將前后配置差發(fā)布了一個
EnvironmentChangeEvent
事件。而EnvironmentChangeEvent的監(jiān)聽器是由ConfigurationPropertiesRebinder實現(xiàn)的,其主要邏輯在
rebind
方法。搜索公眾號后端架構(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)部存儲的
ConfigurationPropertiesBeans
依次執(zhí)行銷毀邏輯,再執(zhí)行初始化邏輯實現(xiàn)屬性的重新綁定。這里可以知道,SpringCloud在進行配置刷新時是考慮過ConfigurationProperties的,經(jīng)過測試,在ContextRefresher刷新上下文后,ConfigurationProperties注解類的屬性是會進行動態(tài)刷新的。測試一次就解決的事情,感覺有些白忙活了。。不過既然查到這里了,就再往下深入一些。Bean的創(chuàng)建與環(huán)境接著我們再來看一下,環(huán)境里的屬性都是怎么在
Bean創(chuàng)建時被使用的。我們知道,Spring的Bean都是在BeanFactory內(nèi)創(chuàng)建的,創(chuàng)建邏輯的入口在
AbstractBeanFactory.doGetBean(name,requiredType,args,false)
方法,而具體實現(xiàn)在
AbstractAutowireCapableBeanFactory.doCreateBean
方法內(nèi),在這個方法里,實現(xiàn)了Bean實例的創(chuàng)建、屬性填充、初始化方法調(diào)用等邏輯。在這里,有一個非常復(fù)雜的步驟就是調(diào)用全局的
BeanPostProcessor,這個接口是Spring為Bean創(chuàng)建準備的勾子接口,實現(xiàn)這個接口的類可以對Bean創(chuàng)建時的操作進行修改。它是一個非常重要的接口,是我們能干涉SpringBean創(chuàng)建流程的重要入口。我們要說的是它的一種具體實現(xiàn)
ConfigurationPropertiesBindingPostProcessor,它通過調(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);
}找到對應(yīng)的屬性后,再使用converter將屬性轉(zhuǎn)換為對應(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屬性的動態(tài)修改的,但在查詢流程時,我也找到了一種比較trick的方式。我們先來整理動態(tài)屬性注入的關(guān)鍵點,再從這些關(guān)鍵點里找可修改點。PropertySourceLocator將PropertySource從遠程數(shù)據(jù)源引入,如果這時我們能修改數(shù)據(jù)源的結(jié)果就能達到目的,可是SpringCloud的遠程資源定位器ConfigServicePropertySourceLocator和遠程調(diào)用工具RestTemplate都是實現(xiàn)類,如果生硬地對其繼承并修改,代碼很不優(yōu)雅。Bean創(chuàng)建時會依次使用BeanPostProcessor對上下文進行操作。這時添加一個BeanPostProcessor,可以手動實現(xiàn)對Bean屬性的修改。但這種方式實現(xiàn)起來很復(fù)雜,而且由于每一個BeanPostProcessor在所有Bean創(chuàng)建時都會調(diào)用,可能會有安全問題。Spring會在解決類屬性注入時,使用PropertyResolver將配置項解析為類屬性指定的類型。這時候添加屬性解析器PropertyResolver或類型轉(zhuǎn)換器ConversionService可以插手屬性的操作。但它們都只負責(zé)處理一個屬性,由于我的目標是”多個”屬性變成一個屬性,它們也無能為力。我這里能想到的方式是借用Spring自動注入的能力,把EnvironmentBean注入到某個類中,然后在類的初始化方法里對Environment內(nèi)的PropertySource里進行修改,也可以達成目的,這里貼一下偽代碼。@Component
@RefreshScope
//
借用
Spring
Cloud
實現(xiàn)此
Bean
的刷新
public
class
ListSupportPropertyResolver
{
@Autowired
ConfigurableEnvironment
env;
//
將環(huán)境注入到
Bean
內(nèi)是修改環(huán)境的重要前提
@PostConstruct
public
void
init()
{
//
將屬性鍵值對從環(huán)境內(nèi)取出
Map
properties
=
extract(e
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 廠房室外電纜施工方案
- 鄭州黃河生態(tài)廊道施工方案
- 常州景區(qū)仿木護欄施工方案
- 宣城交通標識標牌施工方案
- 鐵皮管道保溫現(xiàn)場施工方案
- 植被清理施工方案
- 西雙版納廚房防水施工方案
- 輸電線路綠色施工方案
- 莆田防盜鑄鐵井蓋施工方案
- 天津生態(tài)餐廳溫室施工方案
- DB-T29-74-2018天津市城市道路工程施工及驗收標準
- 小學(xué)一年級20以內(nèi)加減法混合運算3000題(已排版)
- 智慧工廠數(shù)字孿生解決方案
- 病機-基本病機 邪正盛衰講解
- 品管圈知識 課件
- 非誠不找小品臺詞
- 2024年3月江蘇省考公務(wù)員面試題(B類)及參考答案
- 患者信息保密法律法規(guī)解讀
- 老年人護理風(fēng)險防控PPT
- 充電樁采購安裝投標方案(技術(shù)方案)
- 醫(yī)院科室考勤表
評論
0/150
提交評論