05-Spring初級(jí)容器初始化:基礎(chǔ)數(shù)據(jù)的準(zhǔn)備

開篇
上一節(jié),我們以XmlBeanFactory的構(gòu)造方法作為入口,到它的父類中看了一下,發(fā)現(xiàn)它通過方法ignoreDependencyInterface將BeanNameAware、BeanFactoryAware和BeanClassLoaderAware這個(gè)三個(gè)感知接口添加到一個(gè)Set集合中。
目的也是非常簡(jiǎn)單,就是讓BeanName、BeanFactory和BeanClassLoader這些資源,只能通過Spring內(nèi)部調(diào)用這些接口方法來(lái)注入,而不能通過xml配置文件或其他的方式注入。
接下來(lái),我們繼續(xù)看下XmlBeanFactory的構(gòu)造方法中還有哪些邏輯,這一節(jié)主要包括以下幾個(gè)部分:
1.繼續(xù)沿著XmlBeanFactory初始化的這條主線,看下資源Resource在哪里被加載的
2.看下EncodedResource是什么,為什么需要它來(lái)包裝原始的Resource資源
3.最后看下實(shí)際加載Resource資源之前,還需要準(zhǔn)備那些基礎(chǔ)數(shù)據(jù)
初探資源Resource加載的入口
現(xiàn)在,我們?cè)倩氐絏mlBeanFactory的構(gòu)造方法中看下:

可以看到,下一步就是委托成員變量reader也就是XmlBeanDefinitionReader中的loadBeanDefinition來(lái)加載Resource資源了。
為了方便大家理解方法loadBeanDefinition的語(yǔ)義,在Spring容器中的每個(gè)bean的屬性、普通方法、構(gòu)造方法等bean相關(guān)的信息,都會(huì)封裝在數(shù)據(jù)結(jié)構(gòu)BeanDefinition中。
bean在最初注冊(cè)到Spring容器時(shí)是以BeanDefinition的形式存在的,后面我們對(duì)BeanDefinition會(huì)更進(jìn)一步的講解的。
EncodedResource是什么呢?
我們繼續(xù)沿著loadBeanDefinitions方法,一步步來(lái)看下:

可以看到,在loadBeanDefinition方法中,首先將Resource資源封裝為了EncodedResource,那EncodedResource有什么特別的地方呢?我們進(jìn)去看下:

可以看到,在EncodedResource類中包含了三個(gè)成員變量,分別是Resource類型的resource,很明顯resource就是用來(lái)存放我們傳遞進(jìn)來(lái)的資源的。
最關(guān)鍵的就在于encoding和charset這個(gè)兩個(gè)成員變量,通過名稱我們可以知道就是編碼和字符集,加上EncodedResource的命名方式,我們可以推斷出這個(gè)類就是在確定Resource資源的編碼和字符集信息。
我們進(jìn)入到EncodedResoruce的構(gòu)造方法中來(lái)看下:

可以看到,EncodedResource的構(gòu)造方法中,其實(shí)就是將resource對(duì)象設(shè)置到自己的成員變量resource中,同時(shí)還傳了兩個(gè)null值給到構(gòu)造方法,也就是說(shuō)encoding和charset默認(rèn)值為null。
簡(jiǎn)單到EncodedResource類中看一下發(fā)現(xiàn),使用到encoding和charset的也就是一個(gè)地方也就是getReader方法:

可以看到在getReader方法中,首先會(huì)通過getInputStream方法獲取resource的輸入流,同時(shí)會(huì)指定字符集或者編碼來(lái)創(chuàng)建InputStreamReader。
但是,現(xiàn)在我們傳入的charset和encoding都為null,所以創(chuàng)建的就是一個(gè)沒有指定字符集和編碼的InputStreamReader了。
InputStreamReader大家或多或少都了解過一點(diǎn),就是Java SE IO流中將字節(jié)輸入流轉(zhuǎn)換為字符輸入流的API,很明顯接下來(lái)Spring將會(huì)以字符流的方式,讀取我們的資源Resource。
基礎(chǔ)數(shù)據(jù)的準(zhǔn)備工作
了解完EncodedResource之后,我們?cè)倮^續(xù)回到剛才的位置:

可以看到,將Resource資源封裝為帶字符集和編碼類型的EncodedResource之后,現(xiàn)在繼續(xù)調(diào)用XmlBeanDefinitionReader中的另外一個(gè)重載方法loadBeanDefinitions,我們進(jìn)去看下:

前面的一些瑣碎的代碼,我們可以先跳過,可以看到首先會(huì)通過encodedResource調(diào)用getResource方法,獲取我們剛才設(shè)置進(jìn)去的Resource。
而前面我們通過Resource的類繼承圖可以知道,Resource是繼承了InputStreamSource的,所以我們可以調(diào)用InputStreamSource中的getInputStream方法,獲取Resource資源中的輸入流,接下來(lái),我們可以看到將輸入流封裝為了InputSource,然后再調(diào)用doLoadBeanDefinitions方法。
直到這一步,我們的數(shù)據(jù)準(zhǔn)備工作才算完成了,從doLoadBeanDefinitions方法開始,我們就要正式開始解析xml配置文件了。
我們先一睹為快,看下doLoadBeanDefinitions方法里在干些什么事情:

可以看到,doLoadBeanDefinitions方法中大體來(lái)說(shuō)就干了兩件事,一是通過我們傳進(jìn)來(lái)的參數(shù)inputSource和resource創(chuàng)建xml文件的Document對(duì)象,另外就是解析Document對(duì)象將解析到的bean注入到Spring的容器中。
至于Document是什么以及如何解析,我們后面會(huì)詳細(xì)講解,并且這兩個(gè)步驟涉及到的邏輯也是比較復(fù)雜的。
總結(jié)
好了,今天的知識(shí)點(diǎn),我們就講到這里了,我們來(lái)總結(jié)一下吧。
一張圖來(lái)梳理下當(dāng)前的流程:

這一節(jié),我們繼續(xù)跟進(jìn)XmlBeanFactory的構(gòu)造方法,除了上一節(jié)提到了的,將一些感知接口添加到集合中之外,我們還發(fā)現(xiàn)XmlBeanFactory會(huì)將Resource的加載任務(wù)委托給XmlBeanDefinitionReader。
XmlBeanDefinitionReader首先會(huì)將Resource封裝為EncodedResource,EncodedResource相比于我們傳進(jìn)來(lái)的Resource只不過多了一些字符集和編碼相關(guān)的設(shè)置,然后通過Resource中的輸入流創(chuàng)建了InputSource,接下來(lái)進(jìn)入到真正加載資源的方法doLoadBeanDefinitions中。
下一節(jié),我們來(lái)看下Spring到底是如何加載資源Resource的吧。