欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot解析yml全流程詳解

 更新時間:2022年03月08日 10:48:03   作者:碼農(nóng)參上  
本文主要介紹了SpringBoot解析yml全流程詳解,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

背景

前幾天的時候,項(xiàng)目里有一個需求,需要一個開關(guān)控制代碼中是否執(zhí)行一段邏輯,于是理所當(dāng)然的在yml文件中配置了一個屬性作為開關(guān),再配合nacos就可以隨時改變這個值達(dá)到我們的目的,yml文件中是這樣寫的:

switch:
  turnOn: on

程序中的代碼也很簡單,大致的邏輯就是下面這樣,如果取到的開關(guān)字段是on的話,那么就執(zhí)行if判斷中的代碼,否則就不執(zhí)行:

@Value("${switch.turnOn}")
private String on;

@GetMapping("testn")
public void test(){
    if ("on".equals(on)){
        //TODO
    }
}

但是當(dāng)代碼實(shí)際跑起來,有意思的地方來了,我們發(fā)現(xiàn)判斷中的代碼一直不會被執(zhí)行,直到debug一下,才發(fā)現(xiàn)這里的取到的值居然不是on而是true。

看到這,是不是感覺有點(diǎn)意思,首先盲猜是在解析yml的過程中把on作為一個特殊的值進(jìn)行了處理,于是我干脆再多測試了幾個例子,把yml中的屬性擴(kuò)展到下面這些:

switch:
  turnOn: on
  turnOff: off
  turnOn2: 'on'
  turnOff2: 'off'

再執(zhí)行一下代碼,看一下映射后的值:

可以看到,yml中沒有帶引號的onoff被轉(zhuǎn)換成了truefalse,帶引號的則保持了原來的值不發(fā)生改變。

到這里,讓我忍不住有點(diǎn)好奇,為什么會發(fā)生這種現(xiàn)象呢?于是強(qiáng)忍著困意翻了翻源碼,硬磕了一下SpringBoot加載yml配置文件的過程,終于讓我看出了點(diǎn)門道,下面我們一點(diǎn)一點(diǎn)細(xì)說!

因?yàn)榕渲梦募募虞d會涉及到一些SpringBoot啟動的相關(guān)知識,所以如果對這一塊不是很熟悉的同學(xué),可以先提前先看一下Hydra在古早時期寫過一篇文章預(yù)熱一下。下面的介紹中,只會摘出一些對加載和解析配置文件比較重要的步驟進(jìn)行分析,對其他無關(guān)部分進(jìn)行了省略。

加載監(jiān)聽器

當(dāng)我們啟動一個SpringBoot程序,在執(zhí)行SpringApplication.run()的時候,首先在初始化SpringApplication的過程中,加載了11個實(shí)現(xiàn)了ApplicationListener接口的攔截器。

這11個自動加載的ApplicationListener,是在spring.factories中定義并通過SPI擴(kuò)展被加載的:

這里列出的10個是在spring-boot中加載的,還有剩余的1個是在spring-boot-autoconfigure中加載的。其中最關(guān)鍵的就是ConfigFileApplicationListener,它和后面要講到的配置文件的加載相關(guān)。

執(zhí)行run方法

在實(shí)例化完成SpringApplication后,會接著往下執(zhí)行它的run方法。

可以看到,這里通過getRunListeners方法獲取的SpringApplicationRunListeners中,EventPublishingRunListener綁定了我們前面加載的11個監(jiān)聽器。但是在執(zhí)行starting方法時,根據(jù)類型進(jìn)行了過濾,最終實(shí)際只執(zhí)行了4個監(jiān)聽器的onApplicationEvent方法,并沒有我們希望看到的ConfigFileApplicationListener,讓我們接著往下看。

當(dāng)run方法執(zhí)行到prepareEnvironment時,會創(chuàng)建一個ApplicationEnvironmentPreparedEvent類型的事件,并廣播出去。這時所有的監(jiān)聽器中,有7個會監(jiān)聽到這個事件,之后會分別調(diào)用它們的onApplicationEvent方法,其中就有了我們心心念念的ConfigFileApplicationListener,接下來讓我們看看它的onApplicationEvent方法中做了什么。

在方法的調(diào)用過程中,會加載系統(tǒng)自己的4個后置處理器以及ConfigFileApplicationListener自身,一共5個后置處理器,并執(zhí)行他們的postProcessEnvironment方法,其他4個對我們不重要可以略過,最終比較關(guān)鍵的步驟是創(chuàng)建Loader實(shí)例并調(diào)用它的load方法。

加載配置文件

這里的LoaderConfigFileApplicationListener的一個內(nèi)部類,看一下Loader對象實(shí)例化的過程:

在實(shí)例化Loader對象的過程中,再次通過SPI擴(kuò)展的方式加載了兩個屬性文件加載器,其中的YamlPropertySourceLoader就和后面的yml文件的加載、解析密切關(guān)聯(lián),而另一個PropertiesPropertySourceLoader則負(fù)責(zé)properties文件的加載。創(chuàng)建完Loader實(shí)例后,接下來會調(diào)用它的load方法。

loadForFileExtension方法中,首先將classpath:/application.yml加載為Resource文件,接下來準(zhǔn)備正式開始,調(diào)用了之前創(chuàng)建好的YamlPropertySourceLoader對象的load方法。

封裝Node

load方法中,開始準(zhǔn)備進(jìn)行配置文件的解析與數(shù)據(jù)封裝:

load方法中調(diào)用了OriginTrackedYmlLoader對象的load方法,從字面意思上我們也可以理解,它的用途是原始追蹤yml的加載器。中間一連串的方法調(diào)用可以忽略,直接看最后也是最重要的是一步,調(diào)用OriginTrackingConstructor對象的getData接口,來解析yml并封裝成對象。

在解析yml的過程中實(shí)際使用了Composer構(gòu)建器來生成節(jié)點(diǎn),在它的getNode方法中,通過解析器事件來創(chuàng)建節(jié)點(diǎn)。通常來說,它會將yml中的一組數(shù)據(jù)封裝成一個MappingNode節(jié)點(diǎn),它的內(nèi)部實(shí)際上是一個NodeTuple組成的ListNodeTupleMap的結(jié)構(gòu)類似,由一對對應(yīng)的keyNodevalueNode構(gòu)成,結(jié)構(gòu)如下:

好了,讓我們再回到上面的那張方法調(diào)用流程圖,它是根據(jù)文章開頭的yml文件中實(shí)際內(nèi)容內(nèi)容繪制的,如果內(nèi)容不同調(diào)用流程會發(fā)生改變,大家只需要明白這個原理,下面我們具體分析。

首先,創(chuàng)建一個MappingNode節(jié)點(diǎn),并將switch封裝成keyNode,然后再創(chuàng)建一個MappingNode,作為外層MappingNodevalueNode,同時存儲它下面的4組屬性,這也是為什么上面會出現(xiàn)4次循環(huán)的原因。如果有點(diǎn)困惑也沒關(guān)系,看一下下面的這張圖,就能一目了然了解它的結(jié)構(gòu)。

在上圖中,又引入了一種新的ScalarNode節(jié)點(diǎn),它的用途也比較簡單,簡單String類型的字符串用它來封裝成節(jié)點(diǎn)就可以了。到這里,yml中的數(shù)據(jù)被解析完成并完成了初步的封裝,可能眼尖的小伙伴要問了,上面這張圖中為什么在ScalarNode中,除了value還有一個tag屬性,這個屬性是干什么的呢?

在介紹它的作用前,先說一下它是怎么被確定的。這一塊的邏輯比較復(fù)雜,大家可以翻一下ScannerImplfetchMoreTokens方法的源碼,這個方法會根據(jù)yml中每一個keyvalue是以什么開頭,來決定以什么方式進(jìn)行解析,其中就包括了{、[、'、%?等特殊符號的情況。以解析不帶任何特殊字符的字符串為例,簡要的流程如下,省略了一些不重要部分:

在這張圖的中間步驟中,創(chuàng)建了兩個比較重要的對象ScalarTokenScalarEvent,其中都有一個為trueplain屬性,可以理解為這個屬性是否需要解釋,是后面獲取Resolver的關(guān)鍵屬性之一。

上圖中的yamlImplicitResolvers其實(shí)是一個提前緩存好的HashMap,已經(jīng)提前存儲好了一些Char類型字符與ResolverTuple的對應(yīng)關(guān)系:

當(dāng)解析到屬性on時,取出首字母o對應(yīng)的ResolverTuple,其中的tag就是tag:yaml.org.2002:bool。當(dāng)然了,這里也不是簡單的取出就完事了,后續(xù)還會對屬性進(jìn)行正則表達(dá)式的匹配,看與regexp中的值是否能對的上,檢查無誤時才會返回這個tag。

到這里,我們就解釋清楚了ScalarNodetag屬性究竟是怎么獲取到的了,之后方法調(diào)用層層返回,返回到Origi

調(diào)用構(gòu)造器

constructDocument中,有兩步比較重要,第一步是推斷當(dāng)前節(jié)點(diǎn)應(yīng)該使用哪種類型的構(gòu)造器,第二步是使用獲得的構(gòu)造器來重新對Node節(jié)點(diǎn)中的value進(jìn)行賦值,簡易流程如下,省去了循環(huán)遍歷的部分:

nTrackingConstructor父類BaseConstructorgetData方法中。接下來,繼續(xù)執(zhí)行constructDocument方法,完成對yml文檔的解析。

推斷構(gòu)造器種類的過程也很簡單,在父類BaseConstructor中,緩存了一個HashMap,存放了節(jié)點(diǎn)的tag類型到對應(yīng)構(gòu)造器的映射關(guān)系。在getConstructor方法中,就使用之前節(jié)點(diǎn)中存入的tag屬性來獲得具體要使用的構(gòu)造器:

當(dāng)tagbool類型時,會找到SafeConstruct中的內(nèi)部類ConstructYamlBool作為構(gòu)造器,并調(diào)用它的construct方法實(shí)例化一個對象,來作為ScalarNode節(jié)點(diǎn)的value的值:

construct方法中,取到的val就是之前的on,至于下面的這個BOOL_VALUES,也是提前初始化好的一個HashMap,里面提前存放了一些對應(yīng)的映射關(guān)系,key是下面列出的這些關(guān)鍵字,value則是Boolean類型的truefalse

到這里,yml中的屬性解析流程就基本完成了,我們也明白了為什么yml中的on會被轉(zhuǎn)化為true的原理了。

思考

那么,下一個問題來了,既然yml文件解析中會做這樣的特殊處理,那么如果換成properties配置文件怎么樣呢?

sw.turnOn=on
sw.turnOff=off

執(zhí)行一下程序,看一下結(jié)果:

可以看到,使用properties配置文件能夠正常讀取結(jié)果,看來是在解析的過程中沒有做特殊處理,至于解析的過程,有興趣的小伙伴可以自己去閱讀一下源碼。

到此這篇關(guān)于SpringBoot解析yml全流程詳解的文章就介紹到這了,更多相關(guān)SpringBoot解析yml 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot使用mybatis-plus分頁查詢無效的問題解決

    SpringBoot使用mybatis-plus分頁查詢無效的問題解決

    MyBatis-Plus提供了很多便捷的功能,包括分頁查詢,本文主要介紹了SpringBoot使用mybatis-plus分頁查詢無效的問題解決,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • java如何實(shí)現(xiàn)項(xiàng)目啟動時執(zhí)行指定方法

    java如何實(shí)現(xiàn)項(xiàng)目啟動時執(zhí)行指定方法

    這篇文章主要為大家詳細(xì)介紹了java項(xiàng)目如何啟動時執(zhí)行指定方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 為什么SpringBoot的jar可以直接運(yùn)行

    為什么SpringBoot的jar可以直接運(yùn)行

    這篇文章主要介紹了為什么SpringBoot的jar可以直接運(yùn)行,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • SpringCloud Gateway的路由,過濾器和限流解讀

    SpringCloud Gateway的路由,過濾器和限流解讀

    這篇文章主要介紹了SpringCloud Gateway的路由,過濾器和限流解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • Java多線程編程中線程鎖與讀寫鎖的使用示例

    Java多線程編程中線程鎖與讀寫鎖的使用示例

    這篇文章主要介紹了Java多線程編程中線程鎖與讀寫鎖的使用示例,鎖是控制程序多線程并發(fā)的重要手段,需要的朋友可以參考下
    2016-04-04
  • Java的反射機(jī)制之類加載詳解

    Java的反射機(jī)制之類加載詳解

    這篇文章主要介紹了Java的反射機(jī)制之類加載詳解,反射機(jī)制是java實(shí)現(xiàn)動態(tài)語言的關(guān)鍵,也就是通過反射實(shí)現(xiàn)類動態(tài)加載,靜態(tài)加載是指在編譯時期確定要加載的類的類型,即通過class關(guān)鍵字和類名來獲取對應(yīng)類的類型,需要的朋友可以參考下
    2023-09-09
  • java單鏈表實(shí)現(xiàn)書籍管理系統(tǒng)

    java單鏈表實(shí)現(xiàn)書籍管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了java單鏈表實(shí)現(xiàn)書籍管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java中的15種鎖

    Java中的15種鎖

    在讀很多并發(fā)文章中,會提及各種各樣鎖如公平鎖,樂觀鎖等等,這篇文章小編將向大家介紹是各種鎖的分類,感興趣的小伙伴可以參考下面文章的具體內(nèi)容
    2021-09-09
  • Java包機(jī)制及javadoc詳解

    Java包機(jī)制及javadoc詳解

    為了更好地組織類,Java提供了包機(jī)制,用于區(qū)別類名的命名空間,一般利用公司域名倒置作為包名,這篇文章主要介紹了Java包機(jī)制以及javadoc,需要的朋友可以參考下
    2022-10-10
  • SpringBoot整合java診斷工具Arthas解讀

    SpringBoot整合java診斷工具Arthas解讀

    這篇文章主要介紹了SpringBoot整合java診斷工具Arthas,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03

最新評論