14-Spring高級(jí)容器初始化:初探容器ApplicationContext初始化

開篇
通過前面的學(xué)習(xí),我們已經(jīng)了解了Spring初級(jí)容器是如何初始化的了,有了這些知識(shí)儲(chǔ)備之后,我們對(duì)Spring的認(rèn)知就已經(jīng)初步形成了一個(gè)“框架”了,接下來我們要做的事情就是在這個(gè)“框架”基礎(chǔ)之上,去探索Spring的一些更高級(jí)的功能特性,這樣的話我們對(duì)Spring的認(rèn)知就會(huì)更加立體和完整了。
相比于陌生的XmlBeanFactory而言,ApplicationContext顯然大家更加的熟悉,在Spring高級(jí)容器ApplicationContext中,一方面包含了初級(jí)容器XmlBeanFactory中的所有的東西。
ApplicationContext在初級(jí)容器的基礎(chǔ)之上,擴(kuò)展了非常多的高級(jí)特性,同時(shí)也給我們提供了非常多的功能拓展點(diǎn),通過這些功能拓展點(diǎn),我們可以將Spring這個(gè)框架的功能改造的更加滿足于我們實(shí)際的業(yè)務(wù)場(chǎng)景。
比如,我們或多或少就聽說過Spring前置處理器及后置處理器等功能,其實(shí)就是在ApplicationContext中擴(kuò)展的一個(gè)高級(jí)功能。
從這一節(jié)開始呢,我們就一起來探索下Spring高級(jí)容器ApplicationContext是如何初始化的吧,這一節(jié)主要包括以下幾個(gè)部分內(nèi)容:
1.簡(jiǎn)單來使用一下ApplicationContext,作為源碼分析的入口
2.接下來再來看下ApplicationContext對(duì)環(huán)境變量以及路徑占位符解析的準(zhǔn)備工作
3.最后再來定位下ApplicationContext中,最核心的方法是在什么位置,為下一步的源碼分析做準(zhǔn)備
ApplicationContext簡(jiǎn)單的使用
首先,我們先來看下ApplicationContext是如何使用的吧:

可以看到,同樣是解析xml文件,如果我們使用ApplicationContext來解析的話,也就是將之前的XmlBeanFactory替換成了ApplicationContext的實(shí)現(xiàn)類ClassPathXmlApplicationContext,運(yùn)行一下和XmlBeanFactory一樣,可以在控制臺(tái)上打印出字符串“ruyuan ”。
當(dāng)然,ApplicationContext接口的實(shí)現(xiàn)類是在是太多了,但是底層的一些代碼邏輯都是通用的,我們這里就采用ClassPathXmlApplicationContext作為ApplicationContext初始化源碼分析的入口。
初始化環(huán)境變量相關(guān)的信息
通過ClassPathXmlApplicationContext的名稱,我們可以知道它是從classpath路徑下加載和解析applicationContext.xml的。
但是,我們并沒有看到它直接使用Resource資源來封裝xml,可以預(yù)先做一個(gè)合理的猜想,也就是說ClassPathXmlApplicationContext在底層,會(huì)根據(jù)我們傳進(jìn)去的xml文件名加載資源Resource。
帶著這個(gè)猜想,我們以ClassPathXmlApplicationContext的構(gòu)造方法為入口開始分析:

可以看到,在ClassPathXmlApplicationContext的構(gòu)造方法中,首先會(huì)把xml文件的名稱封裝成一個(gè)String類型的數(shù)組,然后帶上參數(shù)refresh的值true和參數(shù)parent值null,繼續(xù)調(diào)用ClassPathXmlApplicationContext另外的一個(gè)重載構(gòu)造方法。
我們到ClassPathXmlApplicationContext的重載構(gòu)造方法中看下:

可以看到,首先調(diào)用super方法調(diào)用父類的構(gòu)造器,我們層層跟到了父類AbstractApplicationContext時(shí)才發(fā)現(xiàn)一絲蛛絲馬跡,我們發(fā)現(xiàn)這里有兩個(gè)方法。
我們先到this()方法中看下:

可以看到,這里應(yīng)該是在初始化一個(gè)成員變量resourcePatternResolver,那getResourcePatternResolver方法能獲取到什么呢?我們也進(jìn)去看下:

可以看到,成員變量resourcePatternResolver被初始化了PathMatchingResourcePatternResolver類型,根據(jù)名稱我們可以推測(cè)PathMatchingResourcePatternResolver應(yīng)該是匹配路徑的一個(gè)資源解析器,應(yīng)該是獲取我們xml文件資源的一個(gè)組件。
當(dāng)然,知道這個(gè)暫時(shí)對(duì)我們好像沒什么用,我們可以在這里留個(gè)心眼,很有可能后面在某個(gè)地方就用到它了。
繼this()方法之后,我們?cè)俚降诙€(gè)方法setParent中看下:

可以看到往setParent方法中傳入了參數(shù)parent,當(dāng)然,最終也只是為成員變量parent賦值而已,當(dāng)然,前面我們也看到了參數(shù)parent的值是為空的。
就算參數(shù)parent的值不為空,setParent方法中也只是初始化一些環(huán)境相關(guān)的信息,這樣的代碼對(duì)于我們主流程的分析,就顯得不是那么重要了。
XML文件路徑占位符的解析
現(xiàn)在,我們?cè)倩氐紺lassPathXmlApplicationContext構(gòu)造方法的位置:

接下來,我們可以看到以xml文件名封裝的String數(shù)組作為參數(shù),傳入了setConfigLocations方法中。
我們到setConfigLocations方法中看下:

可以看到,在setConfigLocations方法中,通過我們傳入的參數(shù)locations,構(gòu)建了一個(gè)新的String數(shù)組configLocations,用于存放解析后的xml文件路徑。
那方法resolvePath會(huì)怎么解析呢?我們簡(jiǎn)單瞧一下:

我們到方法resolveRequiredPlaceholders中看下:

看到這里,我們大概就明白是怎么回事了。
也就是說,我們傳入的xml路徑參數(shù)path中,如果存在占位符“${}”,那方法resolveRequiredPlaceholders就可以解析占位符,畢竟,你要是拿著帶有占位符“${}”的路徑去獲取xml資源肯定是找不到xml的。
而且,我們通過注釋也看到了,如果xml的路徑中有“${}”占位符,但是卻沒有對(duì)應(yīng)匹配的默認(rèn)屬性值是會(huì)報(bào)錯(cuò)的。
很顯然,我們看到這里還是一些邊邊角角的代碼,依然還沒有進(jìn)入到ApplicationContext的核心代碼,怎么說呢,看Spring源碼差不多就是這樣的一種感覺,第一遍云里霧里、抓耳撓腮甚至經(jīng)?!懊月贰?。
但是,當(dāng)你在第一遍分析源碼的時(shí)候看過一些代碼了,有些邏輯可能當(dāng)下看不懂,但是,當(dāng)你第二遍再回過頭來看的時(shí)候,你會(huì)發(fā)現(xiàn)很多東西瞬間就豁然開朗了,特別是后面我們即將要分析的工廠后處理器以及bean后處理器相關(guān)的源碼。
ApplicationContext初始化的核心方法
現(xiàn)在我們已經(jīng)看到了,Spring會(huì)為我們?cè)O(shè)置進(jìn)去的xml路徑進(jìn)行一些準(zhǔn)備處理,比如解析路徑中的一些占位符,但是具體在什么時(shí)候加載和解析xml文件呢?這個(gè)問題依然值得我們繼續(xù)探索,我們繼續(xù)往下看。
現(xiàn)在,我們?cè)倏吹紺lassPathXmlApplicationContext構(gòu)造方法:

前面我們已經(jīng)看到,參數(shù)refresh的值為true,所以會(huì)調(diào)用refresh方法,那refresh方法到底是在干什么呢?
接下來,我們到refresh方法中看下:

可以看到,容器ApplicationContext初始化的核心的邏輯都在方法refresh當(dāng)中,并且,接下來我們分析ApplicationContext的初始化,核心就是圍繞著refresh方法來展開。
總結(jié)
好了,今天的知識(shí)點(diǎn)我們就講到這里了,我們來總結(jié)一下吧。
第一,我們從Spring初級(jí)容器BeanFactory到Spring的高級(jí)容器ApplicationContext做了一個(gè)過渡,并且,我們通過一個(gè)簡(jiǎn)單案例,開始入手了ApplicationContext的源碼分析。
第二,經(jīng)過初步的分析,我們發(fā)現(xiàn)暫時(shí)還只是解析我們?cè)O(shè)置進(jìn)去的路徑,防止路徑中含有占位符“${}”,不管怎樣,我們已經(jīng)找到了ApplicationContext容器初始化的關(guān)鍵方法refresh,接下來,我們重點(diǎn)就要開始分析最核心的refresh方法 了。
雖然這一節(jié)的內(nèi)容并不是很多,但是,好在我們也是找到了ApplicationContext初始化的核心方法了,接下來,我們會(huì)按照方法refresh中羅列的這些功能點(diǎn),依次來分析下它們。
如果大家把關(guān)于refresh方法中的一系列源碼章節(jié)的內(nèi)容都掌握了,Spring IOC容器這塊的內(nèi)容就已經(jīng)了然于胸了,后面我們?cè)俜治鯾ean加載的環(huán)節(jié),一切就會(huì)變得水到渠成。