天天看點

Spring源碼解析(二)

這篇文章是接着《Spring源碼解析(一)》寫的,建議讀者先去看看Spring源碼解析(一),這樣循序漸進收獲會更好

通路位址https://blog.csdn.net/dachengzi159/article/details/81180656

擷取XML的驗證模式

了解XML檔案的讀者都應該知道XML檔案的驗證模式保證了XML檔案的正确性,而比較常見的驗證模式有兩種:DTD和XSD。

DTD即文檔類型定義,是一種XML限制模式語言,是XML檔案的驗證機制,屬于XML檔案組成的一部分。

DTD 是一種保證XML文檔格式正确的有效方法,可以通過比較XML文檔和DTD檔案來看文檔是否符合規範,元素和标簽使用是否正确。 一個 DTD文檔包含:元素的定義規則,元素間關系的定義規則,元素可使用的屬性,可使用的實體或符号規則。

DTD和XSD相比:DTD 是使用非 XML 文法編寫的。

DTD 不可擴充,不支援命名空間,隻提供非常有限的資料類型 .

XML Schema語言也就是XSD。XML Schema描述了XML文檔的結構。

可以用一個指定的XML Schema來驗證某個XML文檔,以檢查該XML文檔是否符合其要求。文檔設計者可以通過XML Schema指定一個XML文檔所允許的結構和内容,并可據此檢查一個XML文檔是否是有效的。XML Schema本身是一個XML文檔,它符合XML文法結構。可以用通用的XML解析器解析它。

一個XML Schema會定義:文檔中出現的元素、文檔中出現的屬性、子元素、子元素的數量、子元素的順序、元素是否為空、元素和屬性的資料類型、元素或屬性的預設 和固定值。

XSD是DTD替代者的原因,一是據将來的條件可擴充,二是比DTD豐富和有用,三是用XML書寫,四是支援資料類型,五是支援命名空間。

了解了DTD與XSD的差別後我們再去分析Spring中對于驗證模式的提取就更容易了解了。通過對XmlBeanDefinitionReader類的解讀,鎖定Spring通過getValidationModeForResource方法擷取對應資源的驗證模式。

如果手動指定了驗證模式則使用指定的驗證模式

Spring源碼解析(二)

如果沒有指定就使用自動檢測

Spring源碼解析(二)

 方法的實作其實還是很簡單的,無非是如果設定了驗證模式則使用設定的驗證模式(可以通過對調用XmlBeanDefinitionReader中的setValidationMode方法進行設定),否則使用自動檢測的方式。而自動檢測驗證模式的功能是在函數detectValidationMode方法中實作的,在detectValidationMode函數中又将自動檢測的功能委托給專門處理類validationModeDetector,調用detectValidationMode方法,具體代碼如下:

Spring源碼解析(二)

 了解上面的代碼應該不會太難,Spring用來檢測驗證模式的方法就是判斷是否包含DOCTYPE

,如果包含就是DTD,如果不包含就是XSD。

擷取Document

經過了驗證模式準備的步驟就可以進行Document加載了,同樣XmlBeanDefinitionReader類對于文檔讀取并沒有親力親為,而是委托了DocumentLoader去執行,這裡的DocumentLoader是個接口,而真正調用的是DefaultDocumentLoader。

Spring源碼解析(二)
Spring源碼解析(二)

 通過SAX解析XML文檔的套路大緻都差不多,首先建立createDocumentBuilderFactory,再通過DocumentBuilderFactory建立DocumentBuilder,進而解析inputSource來傳回Document對象。這裡有必要提及一下EntityResolver,對于參數entityResolver,傳入的是通過getEntityResolver()函數擷取的傳回值。

Spring源碼解析(二)

 解析并注冊BeanDefinitions

 當把檔案轉化為Document後,接下來的提取及注冊bean就是我們的重頭戲了

Spring源碼解析(二)

 其中的參數是通過上面的loadDocument加載轉化出來的。在這個方法中很好的應用了面向對象中單一職責的原則,将邏輯處理委托給單一的類進行處理,這個邏輯處理類就是BeanDefinitionDocumentReader。BeanDefinitionDocumentReader是一個接口,執行個體化工作是在createBeanDefinitionDocumentReader()中完成的,而通過此方法,BeanDefinitionDocumentReader真正的類型其實已經是DefaultBeanDefinitionDocumentReader了,進入DefaultBeanDefinitionDocumentReader後,發現這個方法的重要目的就是提取root,以便再次将root作為參數繼續BeanDefinition的注冊。(Spring的命名就像我開始說的那樣,就像講故事一樣,老是記不住)。

Spring源碼解析(二)

終于到了核心邏輯的底層doRegisterBeanDefinitions(root),doRegisterBeanDefinitions(root)開始真正地進行解析了,我們期待的核心部分真正開始了。

Spring源碼解析(二)

重點我已經勾畫出來了。

首先是對profile的處理,然後開始解析,可是當我們跟進preProcessXml(root)和postProcessXml(root)發現代碼是空的,既然是空的還有什麼用呢?我是這樣了解的。一個類要麼是面向繼承而設計的,要麼就是用final修飾的。在DefaultBeanDefinitionDocumentReader中并沒有用final修飾,是以它是面向繼承而設計的。那麼我就不賣關子了,這是模闆方法模式,如果繼承自DefaultBeanDefinitionDocumentReader的子類需要在bean解析前後做一些處理的話,那麼隻需重寫這兩個方法就可以了。

profile屬性的使用

這個屬性沒有用過,不知道怎麼說,我查找了相關資料,大概總結一下吧:

首先程式會擷取beans節點是否定義了profile屬性,如果定義了就到環境變量中去查找,是以需要首先确定environment不可能為空,因為profile是可以同時指定多個的,需要程式對其拆分,并解析每個profile是都符合環境變量中所定義的,不定義則不會浪費性能去解析。

解析并注冊BeanDefinition

處理了profile後就可以進行XML的讀取了,跟蹤代碼進入parseBeanDefinitions(root,this.delegate)。

Spring源碼解析(二)

上面的代碼看起來邏輯還是蠻清楚的,因為在Spring的XML配置裡面有兩大類Bean聲明,一個是預設的,如:

<bean id="myTestBean" class="test.hechen.spring.MyTestBean"/>

另一類就是自定義的:

<tx:annotation-driven>

而這兩種方式的讀取及解析差别還是非常大的,如果采用預設的配置,Spring當然知道該怎麼做,但是如果是自定義的,那麼就需要使用者實作一些接口及配置了。對于根節點或者子節點如果是預設命名空間的話則采用parseDefaultElement方法進行解析,否則使用delegate.parseCustomElement方法對自定義命名空間進行解析。而判斷是否預設命名空間還是自定義命名空間的辦法其實是使用node.getNamespaceURI()擷取命名空間,并與Spring中固定的命名空間http://www.Springframework.org/schema/beans進行比對。如果一緻則認為是預設,否則就是自定義。bean的加載這一塊很難用一個小分類去講解,我将用另一個分類去解析。

碼字不易,如需轉載請标明出處。