06-Spring初級(jí)容器初始化:加載XML的Document

開篇
上一節(jié),我們從XmlBeanFactory的構(gòu)造方法出發(fā),發(fā)現(xiàn)Resource資源的加載交給了XmlBeanDefinitionReader,并且,我們看到的暫時(shí)都是一些數(shù)據(jù)準(zhǔn)備工作,比如將Resource封裝為具有字符集和編碼功能的EncodedResource,然后通過Resource中的輸入流InputStream,又封裝了InputSource。
我們可以知道的是,資源Resource的核心加載環(huán)節(jié)一直都還沒有開始,從這一節(jié)開始我們來看下Resource是如何加載的,主要包括以下幾個(gè)部分:
1.沿著上節(jié)課的思路繼續(xù)分析,看下Spring具體是如何解析xml的
2.通過一個(gè)簡單的案例,體驗(yàn)下解析xml文件的一種方式也就是DOM解析
3.回過頭來看下源碼,看下Spring是通過什么樣的方式來加載xml文件的
Spring是如何解析XML的呢?
好了,我們接著上一節(jié)的方法doLoadBeanDefinitions繼續(xù)分析:

我們可以看到,在方法doLoadBeanDefinitions中,首先將封裝好的inputSource及資源resource傳遞進(jìn)了doLoadDocument方法中,通過方法的名稱應(yīng)該是把resource資源加載成一個(gè)Document對(duì)象,確實(shí)我們也看到了它返回了一個(gè)Document對(duì)象。
我們先到doLoadDocument方法中看下:

可以看到,XmlBeanDefinitionReader又將加載資源resource的任務(wù),委托給了成員變量documentLoader來完成。那documentLoader又是什么呢?我們可以來看下這個(gè)成員變量:

可以看到,成員變量documentLoader的類型為DefaultDocumentLoader,通過類的名稱,我們初步可以推測(cè)出它就是用來加載Document的一個(gè)組件。當(dāng)然現(xiàn)在我們連Document都不是很了解,這些我們先留著等下來揭秘。
我們順勢(shì)到loadDocument方法中看下:

看到這里幾乎就真相大白了,Spring其實(shí)就是通過DOM來解析xml文件的,可能有些同學(xué)之前還不太了解DOM,接下來,我們通過一個(gè)案例帶大家來體驗(yàn)一下DOM是如何解析XML文件的。
XML解析的示例:DOM解析
首先,我們先寫一個(gè)簡單的類Student:

可以看到非常的簡單,Student類中就兩個(gè)字段,分別是String類型的name,和int類型的age,然后我們?cè)俣x一個(gè)xml文件student.xml,內(nèi)容如下:

可以看到,在student.xml中,我們自定義了一些標(biāo)簽,其中student、name和age,分別對(duì)應(yīng)Student類中的name和age字段。
接下來,我們?cè)偻ㄟ^代碼來演示下如何用DOM來解析student.xml:

首先,將xml文件的的絕對(duì)路徑,也就是xml文件在磁盤的位置,作為方法getDocument參數(shù)傳遞進(jìn)去,然后通過DocumentBuilderFactory的newInstance方法,創(chuàng)建出一個(gè)DocumentBuilderFactory,再通newDocumentBuilder方法得到DocumentBuilder對(duì)象。
可以看到,通過DocumentBuilder的parse方法解析xml文件,可以得到xml文件對(duì)應(yīng)的Document對(duì)象,Document對(duì)象中就包含了student.xml中配置的所有信息。
方法getDocument暫時(shí)只是將指定路徑下的xml文件加載成了Document對(duì)象,那具體如何解析Document對(duì)象,獲取xml中配置的信息呢?我們?cè)偻ㄟ^另外一個(gè)方法來解析下Document:

可以看到,在getStudents方法中,我們調(diào)用剛才的getDocument方法先獲取student.xml對(duì)應(yīng)的Document對(duì)象,然后獲取配置文件中的所有student標(biāo)簽依次來遍歷它們,通過對(duì)student標(biāo)簽的解析獲取標(biāo)簽中name標(biāo)簽和age標(biāo)簽中的數(shù)據(jù),并封裝到Student對(duì)象中返回。
最后,我們?cè)贉y(cè)試下剛才寫的這些方法:

運(yùn)行之后,打印結(jié)果如下:

可以發(fā)現(xiàn),通過DOM已經(jīng)成功解析了student.xml配置文件,并將標(biāo)簽中的信息封裝到了Student對(duì)象中并打印了出來。
關(guān)于DOM其他更高階的API大家可以網(wǎng)上搜索一下,通過DOM方式解析xml文件其實(shí)無非也這樣,而Spring對(duì)標(biāo)簽的解析,如bean標(biāo)簽其實(shí)也是通過這樣的方式,因?yàn)镾pring中定義的標(biāo)簽種類非常的多,所以Spring相比于我們這個(gè)案例而言,解析xml的過程會(huì)復(fù)雜的多。
在后面的源碼分析環(huán)節(jié),我們會(huì)跟大家一起來研究下Spring是如何解析各種讓人眼花繚亂的標(biāo)簽的。
Spring是如何加載Resource的呢?
了解完DOM解析xml文件是怎么回事之后,我們?cè)倩氐絼偛诺腟pring源碼位置:

可以看到,Spring源碼中的DOM相關(guān)的代碼和剛才案例中的代碼幾乎是一樣的。
首先創(chuàng)建DocumentBuilderFactory,然后通過DocumentBuilderFactory來進(jìn)一步將inputSource封裝成Document,inputSource上一節(jié)我們看到了,里面是封裝了xml對(duì)應(yīng)的輸入流InputStream的。
最后還有一個(gè)疑問,那就是方法createDocumentBuilder被調(diào)用的時(shí)候,參數(shù)entityResolver是什么呢?
如果要回答這個(gè)問題,就會(huì)涉及到另外一個(gè)重要的知識(shí)點(diǎn),也就是Spring的xml文件的校驗(yàn),畢竟你沒有按照一定的規(guī)范和格式去配置xml文件,Spring在解析xml文件的時(shí)候很有可能就會(huì)出錯(cuò),所以在正式解析xml之前Spring需要校驗(yàn)下xml,下一節(jié),我們專門來看下這塊內(nèi)容。
總結(jié)
好了,今天的知識(shí)點(diǎn),我們就講到這里了,我們來總結(jié)一下吧。
這一節(jié),我們開始分析Spring是如何解析xml文件的,我們通過一個(gè)案例,體驗(yàn)了一下DOM解析的過程是怎么樣的,我們發(fā)現(xiàn)其實(shí)Spring也是通過DOM來解析xml文件的。
后面,我們會(huì)詳細(xì)來看下Spring是如何解析xml中各種各樣的標(biāo)簽,但是,最終的目的還是將解析到的bean相關(guān)的信息注入到Spring容器中。