最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

15-Spring高級容器初始化:初始化環(huán)境和容器BeanFactory

2023-06-17 14:24 作者:儒猿課堂  | 我要投稿

開篇

上一節(jié),我們對容器ApplicationContext的初始化環(huán)節(jié)開始了分析,經(jīng)過初步的探查后,我們已經(jīng)找到了初始化的核心方法refresh。

接下來,我們再來看下在refresh方法中做了哪些事情,這一節(jié)主要包括以下幾個部分:

1.首先來看下在ApplicationContext中,會如何初始化當(dāng)前容器上下文的信息

2.接下來看下在ApplicationContext中,是如何初始化初級容器BeanFactory的

3.最后來看下初級容器BeanFactory,在高級容器ApplicationContext中是如何加載xml文件的


初始化容器上下文環(huán)境


我們回到refresh方法:

可以看到在refresh方法中,首先調(diào)用prepareRefresh方法,根據(jù)方法名稱我們可以初步知道是在為容器的初始化做一些準(zhǔn)備工作。


具體在準(zhǔn)備些什么事呢?我們到方法prepareRefresh中看下:

可以看到,在prepareRefresh方法中大多都是一些初始化工作,比如設(shè)置容器開始初始化的時間startupDate、設(shè)置容器狀態(tài)closed為未關(guān)閉狀態(tài)、以及容器狀態(tài)active設(shè)置為激活狀態(tài)等,當(dāng)然這些都是一些細節(jié)了。

其中,方法initPropertySources首先引起了我們的注意,我們進去看下:

可以看到,在方法initPropertySources里面卻是空的,但是,我們驚奇的發(fā)現(xiàn)方法initPropertySources是通過protected關(guān)鍵字修飾的。

顯然,這又是Spring為我們提供的又一個擴展點,是留給子類去擴展實現(xiàn)的,大家從這些小細節(jié)也能看出來,Spring為什么是一個易擴展的框架了吧。


既然方法initPropertySources是留給子類去實現(xiàn)的,那具體是實現(xiàn)什么功能呢?我們從方法的注釋中可以知道,是為了用實際的值來替換掉占位符。

也就是說可能在xml文件中,也有類似“${...}”這樣的占位符存在,可以通過方法initPropertySources為這些占位符中的參數(shù)初始化一些屬性值,方便后續(xù)解析xml文件中的信息。


比如,我們可以自己寫個子類來實現(xiàn)方法initPropertySources:

可以看到,我們寫了一個類NewClassPathXmlApplicationContext繼承ClassPathXmlApplicationContext,并且重寫了方法initPropertySources。

其中,我們可以在系統(tǒng)屬性中添加屬性username,值為zhangsan,這樣的話,如果xml文件中存在占位符“${username}”,Spring就可以拿著這個屬性的值去替換占位符了,簡單來說方法initPropertySources存在的意義也就是這樣。

接下來,我們來看下prepareRefresh方法中的下一個方法:

根據(jù)方法validateRequiredProperties的名稱,大概也可以知道該方法是用來校驗是否需要某個屬性的,當(dāng)然這樣直接翻譯未免有些牽強,我們直接到方法里面一探究竟:

可以看到,方法validateRequiredProperties將校驗的任務(wù)委托給了成員變量propertyResolver的方法validateRequiredProperties了。

我們跟進到validateRequiredProperties中的方法validateRequiredProperties中看下:

可以看到,此時會遍歷集合requiredProperties,我們可以理解requiredProperties存放的是屬性,比如環(huán)境變量,如果哪個環(huán)境變量的值不存在,就會記錄到MissingRequiredPropertiesException中。

當(dāng)for循環(huán)結(jié)束后會再次檢查,如果ex.getMissingRequiredProperties不為空,也就是說存在某個屬性缺少對應(yīng)的值就會拋異常。


根據(jù)這個特性,我們在日常的開發(fā)過程當(dāng)中,可以用它來判斷某個環(huán)境變量是否配置,比如,我們可以用它來判定java的環(huán)境變量是否配置了:

可以看到還是剛才的方法,如果我們在方法initPropertySources中,通過方法setRequiredProperties指定必須設(shè)置環(huán)境變量名稱為“JAVA_HOME”。


也就是說,既然我設(shè)置了一定需要屬性“JAVA_HOME”,那該環(huán)境變量的值一定得要存在,否則getEnvironment().validateRequiredProperties()方法就會去判斷名稱為“JAVA_HOME”的這個環(huán)境變量是否存在對應(yīng)的值。

如果不存在相應(yīng)的環(huán)境變量值,就像我們剛才看到的一樣就會拋出一個異常,很顯然我們通過這個功能特性,就可以在Spring容器初始化的時候,及時檢測出哪些必要的環(huán)境變量有沒有配置好,及時發(fā)現(xiàn)并排除隱患。


為了方便我們梳理思路,我們及時通過流程圖來記錄一下當(dāng)前分析流程:


初始化容器BeanFactory


好了,prepareRefresh方法暫時分析到這里,我們再回到主流程中的refresh方法上:

接下來,我們再到obtainFreshBeanFactory方法中看下:

可以看到,在obtainFreshBeanFactory方法中有兩個方法,分別是refreshBeanFactory和getBeanFactory。


refreshBeanFactory方法好像比較復(fù)雜,可以選擇先到getBeanFactory方法中看一眼:

可以看到方法getBeanFactory中,其實就是直接獲取beanFactory了,為什么能直接獲取到beanFactory呢,我們推測肯定是在getBeanFactory方法前面的refreshBeanFactory方法中就把beanFactory這個Spring初級容器給初始化完成了。

這樣來說的話在不遠處,我們應(yīng)該就可以看到beanFactory初始化相關(guān)的代碼了,事不宜遲,我們趕緊回到前面的方法refreshBeanFactory中看下:

可以看到,首先在if語句中調(diào)用方法hasBeanFactory,應(yīng)該是判斷beanFactory是否存在,我們可以進去瞧一下:

可以看到,果然就是在對成員變量beanFactory進行非空的一個判定,很顯然第一次我們來是沒有創(chuàng)建beanFactory的,所以返回false。


if語句既然不成立,我們直接后面的try語句中看下:

可以看到,方法createBeanFactory創(chuàng)建了一個DefaultListableBeanFactory對象,我們到createBeanFactory方法中看下:

可以看到,在createBeanFactory中就是簡單創(chuàng)建了一個DefaultListableBeanFactory類型的對象。


DefaultListableBeanFactory這個類對于我們而言可能會有點陌生,其實,它就是我們之前分析的Spring初級容器XmlBeanFactory的直接繼承的父類,我們來看一張類繼承圖:

光是Spring初級容器XmlBeanFactory,就有那么多錯綜復(fù)雜類繼承體系,可以看到XmlBeanFactory直接繼承的類就是DefaultListableBeanFactory,所以,我們也可以將DefaultListableBeanFactory理解為就是一個Spring的初級容器了。

所以,看到這里我們就發(fā)現(xiàn)了,ApplicationContext中果然是包含了Spring初級容器的,但是ApplicationContext接下來會在這個初級容器上擴展什么樣的功能呢?我們繼續(xù)往后面看下。

我們繼續(xù)往后面看下:

接下來,我們可以看到調(diào)用了方法customizeBeanFactory,看樣子是來定制化剛剛創(chuàng)建好的容器beanFactory了,我們進去看下:

可以看到,在方法customizeBeanFactory中,其實就是為我們剛創(chuàng)建好的容器beanFactory設(shè)置兩個屬性,分別是allowBeanDefinitionOverriding和allowCircularReferences。


而且,我們仔細看的話還發(fā)現(xiàn)了一些熟悉的東西,比如參數(shù)allowBeanDefinitionOverriding,這不是就是我們之前在Spring容器中注冊BeanDefinition時,用來判斷相同名稱的BeanDefinition是否允許在容器beanDefinitionMap中被覆蓋的嗎。

而另外的一個參數(shù)allowCircularReferences,則是后面我們研究到bean的加載環(huán)節(jié)時,用來判定是否允許多個bean之間存在循環(huán)依賴引用的,這塊內(nèi)容我們后面再來分析,大家可以先留個印象。


加載XML中的BeanDefinition


接下來,我們再來看下refreshBeanFactory方法中的最后一個方法:

光是看著方法loadBeanDefinitions的名稱,大家是不是就覺的異常熟悉了,和我們之前分析過的Spring初級容器初始化的某些地方越來越類似了。

我們到loadBeanDefinitions方法中看下:

果然,我們又找到了ApplicationContext復(fù)用Spring初級容器BeanFactory的證據(jù)了,在方法loadBeanDefinitions中居然和XmlBeanFactory的底層邏輯一樣,也是通過XmlBeanDefinitionReader來解析xml文件的,后面的劇情相信大家都能猜到了吧。


我們可以看到,接下來會為beanDefinitionReader設(shè)置一些環(huán)境對象、類加載器,當(dāng)然我們還看到了它設(shè)置了EntityResolver為ResourceEntityResolver。

不知道大家還記得前面,我們看到了在ResourceEntityResolver的構(gòu)造方法中,為DTD和XSD這兩種xml校驗類型,分別指定了不同類型的解析器去jar包中獲取對應(yīng)的校驗文件,大家如果忘記了可以先去前面溫習(xí)一下。


接下來,我們再到方法initBeanDefinitionReader中看下:


可以看到,在方法initBeanDefinitionReader中其實也沒有什么東西。

但是我們發(fā)現(xiàn)了它也是被protected關(guān)鍵字修飾的,這就意味著方法initBeanDefinitionReader也是Spring為我們提供的一個擴展點,方便子類去實現(xiàn)用于自定義XmlBeanDefinitionReader的。

我們看到loadBeanDefinitions的最后一個方法:

可以看到,最后將beanDefinitionReader作為參數(shù)傳入了方法loadBeanDefinitions中了,我們進去看下:

可以看到,在loadBeanDefinitions方法中先通過方法getConfigResources獲取資源,可以看到方法getConfigResources默認(rèn)返回null。


接下來,我們終于可以看到前面封裝好的xml對應(yīng)的String數(shù)組獲取出來了,并且reader調(diào)用loadBeanDefinitions方法繼續(xù)處理了,可以預(yù)感到接下來就要到我們熟悉的解析xml的位置了。

我們繼續(xù)跟進到方法loadBeanDefinitions中看下:

可以看到,此時依次遍歷處理String數(shù)組中的每個location也就是xml路徑名稱,一路跟下去看看:

可以看到,最后跟到重載方法loadBeanDefinitions(String, Set<Resource>)時,發(fā)現(xiàn)就會根據(jù)xml文件路徑加載xml對應(yīng)的Resource資源了。


沒有任何懸念的,緊接著又調(diào)用另外一個重載方法,我們再來看下:

終于,我們總算是跟到頭了,果然這里就回到了我們之前XmlBeanFactory解析的主流程上來了。


到這里,我們前面的那個猜想也就成立了,也就是說ApplicationContext最終也會去加載applicationContext.xml這個xml文件。

我們可以回憶一下,接下來肯定是根據(jù)Resource去加載對應(yīng)的Document對象,然后解析Document對象,再解析xml中的各種各樣的屬性和標(biāo)簽,然后將解析到的屬性和方法的信息封裝在BeanDefinition中,最后再注冊到Spring容器beanDefinitionMap中。


好了,現(xiàn)在我們再回到之前refreshBeanFactory方法中看下:

可以看到,最終會把beanFactory賦值給成員變量beanFactory了,果然,最開始外層的getBeanFactory方法才能獲取到它,又一個猜想印證了。


而且,我們可以看到下一次再調(diào)用refreshBeanFactory方法時,方法hasBeanFactory肯定就會檢測到beanFactory已經(jīng)存在了,此時if分支中的hasBeanFactory方法返回的結(jié)果就為true。

此時就會先調(diào)用destoryBeans方法,將Spring容器中所有已經(jīng)創(chuàng)建好的bean全都銷毀掉,并且調(diào)用closeBeanFactory方法關(guān)閉掉當(dāng)前的Spring容器beanFactory。

最后和我們剛分析的一樣,重新從try分支開始創(chuàng)建和初始化新的一個Spring容器beanFactory,這個過程我們可以理解為像是refresh刷新了Spring容器一樣。


總結(jié)


好了,今天的知識點我們就講到這里了,我們來總結(jié)一下吧。

一張圖來梳理下當(dāng)前的流程:

這一節(jié),我們了解了一下如何初始化ApplicationContext的環(huán)境,當(dāng)然都是一些環(huán)境變量的設(shè)置,方便后面用于解析xml中相應(yīng)的占位符。

最為關(guān)鍵的是,我們看到了在ApplicationContext的初始化當(dāng)中,會拿著之前設(shè)置好的xml文件名稱到classpath路徑下加載相應(yīng)的資源Resource,然后和我們之前看到的XmlBeanFactory初始化一樣,委托XmlBeanDefinitionReader去解析并注冊相應(yīng)的beanDefinition。


分析到這里,一個初級的Spring容器BeanFactory已經(jīng)得到了,而且xml文件的解析也都在這個初級Spring容器中完成了,接下來ApplicationContext會為這個初級容器beanFactory擴展哪些高階的功能呢?下一節(jié),我們繼續(xù)來看下。


15-Spring高級容器初始化:初始化環(huán)境和容器BeanFactory的評論 (共 條)

分享到微博請遵守國家法律
邻水| 罗定市| 德昌县| 南丹县| 普安县| 黔江区| 茶陵县| 徐闻县| 钟山县| 应城市| 遂平县| 永春县| 嘉义县| 哈尔滨市| 尚义县| 六盘水市| 舞阳县| 通山县| 湖口县| 盐山县| 克什克腾旗| 绵阳市| 仙桃市| 包头市| 稷山县| 文水县| 通化县| 清徐县| 晋中市| 隆尧县| 霍城县| 子洲县| 金门县| 曲靖市| 河北省| 陇南市| 中江县| 若尔盖县| 安新县| 西盟| 揭东县|