關(guān)于 SpringCloud 配置你了解多少_第1頁(yè)
關(guān)于 SpringCloud 配置你了解多少_第2頁(yè)
關(guān)于 SpringCloud 配置你了解多少_第3頁(yè)
關(guān)于 SpringCloud 配置你了解多少_第4頁(yè)
關(guān)于 SpringCloud 配置你了解多少_第5頁(yè)
已閱讀5頁(yè),還剩4頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論