溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring中如何加載Environment

發(fā)布時(shí)間:2021-06-18 17:25:09 來源:億速云 閱讀:328 作者:Leah 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關(guān)Spring中如何加載Environment,小編覺得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

  1. ConversionService(藍(lán)):類型轉(zhuǎn)換服務(wù)

  2. PropertySource(綠):鍵值對(duì)數(shù)據(jù)源

  3. PropertyResolver(紅):鍵值對(duì)服務(wù),包括類型轉(zhuǎn)換

  4. Environment(紫):環(huán)境配置數(shù)據(jù)服務(wù)

Spring中如何加載Environment

1.ConversionService

?提供了類型轉(zhuǎn)換服務(wù),能將源目標(biāo)轉(zhuǎn)換為目標(biāo)類型,同時(shí)提供了管理功能,內(nèi)部維護(hù)了各類型轉(zhuǎn)換映射關(guān)系。其實(shí)從ConversionService和ConverterRegistry接口就能看出該模塊的功能,如下:

Spring中如何加載Environment

?ConversionService接口為主要的對(duì)外功能接口,提供查詢的能力。

Spring中如何加載Environment

?ConverterRegistry接口為主要的管理接口,提供添加和刪除的能力。而ConfigurableConversionService繼承自上面二者,則提供了Converter的CRUD功能。結(jié)構(gòu)上也延續(xù)了Spring固有的風(fēng)格,將執(zhí)行接口作為主要功能對(duì)外提供單一的接口,再通過繼承的方式,以Configurable開頭的子接口,擴(kuò)展出管理功能,使得責(zé)任分離更加立體。

?接下來是GenericConversionService類,該類提供了接口全部實(shí)現(xiàn),下圖展示了其主要實(shí)現(xiàn):

Spring中如何加載Environment

?GenericConversionService結(jié)構(gòu)上可以說是一個(gè)小型的管理系統(tǒng),內(nèi)部維護(hù)了一個(gè)Converters對(duì)象,用于“底層”管理所有的GenericConverter。同時(shí)還維護(hù)了一個(gè)ConcurrentReferenceHashMap用于緩存常用的GenericConverter。

?Converters在存儲(chǔ)GenericConverter時(shí)還進(jìn)行了分類,如果GenericConverter有指定能夠解析的類別(ConvertiablePair:包括SourceType和TargetType)時(shí),則使用一個(gè)LinkedHashMap按Key Value進(jìn)行存儲(chǔ),在存儲(chǔ)時(shí)會(huì)遍歷可解析的類別,將該GenericConverter追加到對(duì)應(yīng)的Value列表末尾,因而可以看到該Map的Value是一個(gè)LinkedList。對(duì)應(yīng)沒有指定能解析的類別的GenericConverter,則直接放到LinkedHashSet維護(hù)的集合中。

?Converters在查詢時(shí)會(huì)遍歷源類型和目標(biāo)類型的組合結(jié)果,以查找匹配的目標(biāo)GenericConverter對(duì)象。如下:

Spring中如何加載Environment

?對(duì)于getRegisteredConverter方法,會(huì)先使用Key從LinkedHashMap中查找是否有匹配的Converter,再遍歷相應(yīng)的Value,查找到能處理的轉(zhuǎn)換器。若Map中無法查到,則遍歷LinkedHashSet,以查到到能處理的轉(zhuǎn)換器。

?由上知道,Converters在查找時(shí)存在多次遍歷列表的過程,在頻率過多時(shí)效率會(huì)比較低下,因而GenericConversionService內(nèi)部維護(hù)了一個(gè)ConcurrentReferenceHashMap提供緩存的功能,該Map提供了同ConcurrentHashMap相同的功能,但是能夠存儲(chǔ)對(duì)應(yīng)的軟引用,從而能在內(nèi)存不足時(shí)自動(dòng)進(jìn)行內(nèi)存回收。在查到轉(zhuǎn)換器時(shí),會(huì)先試著從緩存中查找,如果獲取不到,則會(huì)轉(zhuǎn)而從Converters中查找,當(dāng)從Converters中查找到后便會(huì)put到ConcurrentReferenceHashMap緩存中。

?DefaultConversionService是一個(gè)單例,繼承自GenericConversionService,在初始化后自動(dòng)添加了默認(rèn)的轉(zhuǎn)換器,包括Scalar相關(guān)的、集合相關(guān)的等轉(zhuǎn)換器。

2.PropertySource

?PropertySource代表了一個(gè)包含鍵值對(duì)的數(shù)據(jù)源。從類定義上看,有一個(gè)表示數(shù)據(jù)源名字的name字段,還有一個(gè)表示具體數(shù)據(jù)源泛型T的source字段。而數(shù)據(jù)源的設(shè)置則是通過構(gòu)造方法傳入的,同時(shí)方法提供了通過鍵名獲取鍵值的抽象方法getProperty。此外還有其他抽象方法,如containsProperty等。

?EnumerablePropertySource繼承自PropertySource,增加了getPropertyNames方法,要求子類返回內(nèi)存持有的鍵名列表。同時(shí)實(shí)現(xiàn)了containsProperty方法,通過判斷所給的鍵名是否存在上述返回的鍵名列表中從而判斷是否包含該鍵名。

?MapPropertySource繼承自EnumerablePropertySource,顧名思義,內(nèi)部通過Map維護(hù)各鍵值對(duì)內(nèi)容。類似的還有PropertiesPropertySource,內(nèi)部通過Properties維護(hù)各鍵值對(duì)內(nèi)容。

?SystemEnvironmentPropertySource是MapPropertySource的裝飾器,繼承自MapPropertySource,為其添加了鍵名轉(zhuǎn)換功能,以應(yīng)對(duì)環(huán)境變量、shell參數(shù)的環(huán)境。在通過鍵名獲取鍵值時(shí),會(huì)先根據(jù)原鍵名進(jìn)行查找,查找不到則通過對(duì)鍵進(jìn)行轉(zhuǎn)換再嘗試查找,具體查找過程為:

  1. 通過name查找

  2. 將name中的 . 轉(zhuǎn)換為 _ 查找

  3. 將name中的 – 轉(zhuǎn)換為 _ 查找

  4. 將name中的 . 和 _ 轉(zhuǎn)換為 – 查找

  5. 將name轉(zhuǎn)換為大寫,再進(jìn)行(1) - (4)的過程

?PropertySources的實(shí)現(xiàn)如下,擴(kuò)展了PropertySource接口,將單個(gè)數(shù)據(jù)源的能力擴(kuò)展到了多個(gè)。MutablePropertySources作為PropertySources的實(shí)現(xiàn),內(nèi)部維護(hù)了一個(gè)List對(duì)象,用以存儲(chǔ)多個(gè)數(shù)據(jù)源,并將自身的行為封裝為L(zhǎng)ist。

Spring中如何加載Environment

3.PropertyResolver

?PropertyResolver定義了一系列接口,以提供了對(duì)外根據(jù)鍵名獲取相應(yīng)值的功能,同時(shí)提供了類型轉(zhuǎn)換和占位符替換的功能,是ConversionService和PropertySource的結(jié)合。ConfigurablePropertyResolver接口繼承自PropertyResolver接口,老規(guī)則,擴(kuò)展了設(shè)置的功能,主要是設(shè)置類型轉(zhuǎn)換器和占位符的相關(guān)屬性。

?AbstractPropertyResolver提供了除PropertySource功能外的其余實(shí)現(xiàn)。使用DefaultConversionService作為默認(rèn)的類型轉(zhuǎn)換實(shí)現(xiàn),使用 ${ 和 } 作為占位符的前后綴,使用:作為默認(rèn)值分割符,同時(shí)引入PropertyPlaceholderHelper用于占位符的解析和替換。而getProperty的實(shí)現(xiàn)則留到了了子類PropertySourcesPropertyResolver中,其引入了PropertySources用以維護(hù)多個(gè)鍵值對(duì)數(shù)據(jù)源。獲取指定屬性值過程如下:

Spring中如何加載Environment

?通過遍歷數(shù)據(jù)源的方式,查到對(duì)應(yīng)的值后,會(huì)進(jìn)行占位符的替換,替換完占位符后會(huì)進(jìn)行類型的轉(zhuǎn)換。類型轉(zhuǎn)換直接用的DefaultConversionService,這個(gè)上面已經(jīng)介紹過了,下面介紹占位符替換。

?占位符替換的功能是在PropertyResolver接口中定義的,分為嚴(yán)格和不嚴(yán)格模式,如下:

Spring中如何加載Environment

?resolvePlaceholders為不嚴(yán)格模式,如果沒法替換占位符,則直接忽略,resolveRequiredPlaceholders為嚴(yán)格模式,如果占位符沒法替換則會(huì)拋出異常。如上面說的,AbstractPropertyResolver實(shí)現(xiàn)時(shí)都委托給PropertyPlaceholderHelper的replacePlaceholders方法。

Spring中如何加載Environment

?如上,該方法要求傳入一個(gè)源字符串,同時(shí)提供一個(gè)PlaceholderResolver數(shù)據(jù)源,一遍解析出占位符內(nèi)容后能夠從數(shù)據(jù)源中獲取對(duì)應(yīng)的值。為了保持類功能的單一職責(zé),從而增加了一個(gè)內(nèi)部接口PlaceholderResolver。上面提到,在這個(gè)模塊中的鍵值對(duì)數(shù)據(jù)源都是由PropertySourcesPropertyResolver維護(hù)的,事實(shí)上上面方法截圖的實(shí)現(xiàn)中,getPropertyAsRawString方法也確實(shí)是由PropertySourcesPropertyResolver提供實(shí)現(xiàn)的,下面看下占位符的解析。

Spring中如何加載Environment

?占位符的解析過程如上流程,主要過程為:

  1. 根據(jù)${前綴得到startIndex

  2. 查找跟${前綴配對(duì)的}后綴,如${xxx${yy}z},得到第二個(gè)}后綴的下標(biāo)endIndex

  3. 截取${和}中間的內(nèi)容得到placeholder

  4. 由于placeholder的內(nèi)容可能也可能包含占位符,因而要遞歸處理placeholder,既占位符可以嵌套,內(nèi)層的結(jié)果可以當(dāng)做外層的Key使用

  5. placeholder解析完后,將其作為Key從鍵值對(duì)源中獲取對(duì)應(yīng)的值propVal

  6. 如果propVal值為空,則判斷是否存在:分割符,如果有分割符,則進(jìn)行分割,并使用前端內(nèi)容作為Key再次查找值。若該次查找結(jié)果不為空,則使用該次結(jié)果為propVal的值,否則使用第二段內(nèi)容作為默認(rèn)值

  7. 若第(5)/(6)步中propVal結(jié)果不為空,則判斷從鍵值對(duì)源中獲取的值是否也有占位符,若有占位符,則再次進(jìn)行解析,若沒有,則將結(jié)果替換回原字段中,更新startIndex,繼續(xù)下次解析。

  8. 若第(5)/(6)步中propVal結(jié)果不空,則會(huì)根據(jù)設(shè)置的解析模式來判斷下一步行為,如果未不嚴(yán)格模式,則跳過該次內(nèi)容,更新startIndex,繼續(xù)下一次解析,若為嚴(yán)格模式,則拋出異常,流程結(jié)束。

?下面以一個(gè)例子進(jìn)行演示,如下

Spring中如何加載Environment

輸出結(jié)果為:

Spring中如何加載Environment

?若將解析模式設(shè)置為嚴(yán)格模式則會(huì)拋出異常

4.Environment

?Environment繼承自PropertyResolver接口,增加了Profiles功能,即我們平時(shí)看到的,多環(huán)境特性,能夠在不同環(huán)境下加載不同的配置。ConfigurableEnvironment繼承自Environment,老規(guī)矩,又是添加了修改的擴(kuò)展接口,同時(shí)增加了獲取系統(tǒng)參數(shù)的接口。另外,該接口也繼承自ConfigurablePropertyResolver,有了鍵值對(duì)數(shù)據(jù)源管理、獲取和處理的能力,集合Environment接口的功能,能夠達(dá)到在不同環(huán)境下通過加載不同配置源實(shí)現(xiàn)環(huán)境隔離的效果。

?AbstractEnvironment是ConfigurablePropertyResolver的實(shí)現(xiàn),提供了默認(rèn)的環(huán)境源default,同時(shí)內(nèi)部組合使用PropertySourcesPropertyResolver作為PropertyResolver的實(shí)現(xiàn)。

?它還維護(hù)了一個(gè)MutablePropertySources對(duì)象,用于存儲(chǔ)多個(gè)數(shù)據(jù)源,在Context的父子上下文中,通過merge方法,能夠?qū)⒏干衔闹械沫h(huán)境變量?jī)?nèi)容添加進(jìn)來(在AbstractApplicationContext設(shè)置父Context時(shí),會(huì)將父Environment進(jìn)行合并)。同時(shí)還有一個(gè)方法customizePropertySources,會(huì)在構(gòu)造方法中進(jìn)行調(diào)用,開放給子類添加默認(rèn)的鍵值對(duì)源,如下:

Spring中如何加載Environment

?最后是StandardEnvironment類,繼承自AbstractEnvironment,重寫了customizePropertySources方法,在該方法中添加了系統(tǒng)相關(guān)的屬性和應(yīng)用環(huán)境變量相關(guān)的屬性的鍵值對(duì)源。如下

Spring中如何加載Environment

?而這兩個(gè)數(shù)據(jù)源來自于前面提到的PropertySource實(shí)現(xiàn)。其中,系統(tǒng)相關(guān)屬性SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME的數(shù)據(jù)源來源于System.getProperties(),而應(yīng)用環(huán)境變量相關(guān)屬性SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME則來源于System.getenv()。

以上就是Spring中如何加載Environment,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見到或用到的。希望你能通過這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI