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

下載本文檔

版權(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)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論