首先,在我的這篇博客中已經說到容器是怎么初步實現的,並且要使用XmlBeanDefinitionReader
對象對Xml
文件進行解析,那么Xml
文件是如何進行解析的,將在這片博客中進行一些陳述.
數據准備階段
准備的目的是封裝resource
參數,目的是為了考慮到Resource
可能存在編碼要求的情況,其次,通過SAX
讀取XML
文件的方式來准備InputSource
對象,最后將參數傳遞到最核心的實現部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())
封裝Resource
調用XmlBeanDefinitionReader
的loadBeanDefinitions(Resource resource)
方法時,首先將resource對象進行再次封裝成EncodedResource
,查看源碼可以發現里面增加了字符集和編碼的封裝,從命名上來看也可以體現出來,將資源封裝完成后,就調用重載的同名函數loadBeanDefinitions(EncodedResource resource)
進行正式的解析.
數據准備操作
在重載方法里面首先通過Set<EncodedResource> currentResources
屬性來記錄已經加載的資源,其次,從EncodedResource
對象中獲取封裝好的Resource
對象,並獲取其inputStream
,將獲取到的輸入流與SAX解析的InputSource
綁定,接下來就進入到了核心的實現部分:doLoadBeanDefinitions(inputSource,encodedResource.getResource())
核心實現
核心部分有兩個關鍵步驟:
- 調用
doLoadDocument(inputSource.resource)
方法獲取Document
- 根據返回的
Document
信息注冊Bean
信息
這兩個步驟支持着整個Spring
容器部分的實現基礎
獲取Document
進入方法體后,將Document
的創建交給DefaultDocumentLoader documentLoader
屬性的loadDocument()
方法,該方法聲明如下:
Document loadDocument(
InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
throws Exception;
調用情況:
documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware())
-
InputSource
:SAX
解析需要使用到的對象 -
EntityResolver
:它的作用是項目本身就可以提供一個如何尋找DTD
聲明的方法,由程序來實現尋找DTD
聲明的過程,將DTD
文件放到項目中某處,在實現時直接將此文檔讀取並返回給SAX
即可,避免了必須通過網絡來尋找相應的聲明.
在這個接口中定義了一個方法
InputSource resolveEntity (String publicId,String systemId)
throws SAXException, IOException;
如果解析的驗證模式是XSD
:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd>
那么.此時得到的兩個參數值分別是:
publicId
:null
systemId
:http://www.springframework.org/schema/beans/spring-beans.xsd
如果解析的驗證模式是DTD
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN 2.0//EN" "http://www.Springframework.org/dtd/Spring-beans-2.0.dtd"
那么,此時得到的兩個參數值分別是:
publicId
😕/Spring//DTD BEAN 2.0//EN
systemId
:http://www.Springframework.org/dtd/Spring-beans-2.0.dtd
而對於不同的驗證模式,Spring
使用了不同的解析器,當使用DTD
驗證時,Spring
會截取后面的*.dtd
,並直接到當前目錄去尋找,當使用XSD
驗證時,Spring
會到META-INF/Spring.schemas
文件中去匹配相應的systemId
並加載對應的XSD
文件
validationMode
:驗證模式
首先,為了保證XML
文件的正確性,有常見兩種驗證模式:DTD
、XSD
兩種驗證模式的區別
我對這兩種的區別目前還不是很詳細,只能簡略的給出定義,但我看到的最直觀的區別是,DTD
驗證需要單獨寫出一個標簽<!DOCTYPE ...>
,而XSD
驗證會將信息寫入<beans xmlns="...">
結點
DTD
DTD
(Document Type Definition)即文檔類型定義,是一種保證XML
文檔格式正確的有效方法,可以通過比較XML
文檔和DTD
文件來看文檔是否符合規范.
XSD
XML Schema
語言就是XSD
(XML Schema Definition),描述了XML
文檔的結構,可以用一個指定的XML Schema
來驗證XML
文檔,以檢查文檔是否符合要求.
驗證模式的讀取
驗證模式的讀取非常簡單,在getValidationModeForResource(resource)
方法中先獲取當前設定的驗證模式是不是自動選擇,源碼中是這么解釋的since we cannot find a clear indication
,當找不到一個確切的驗證模式時,采用這種方式,然后判斷當前resource
對象中采用的是什么驗證模式,通過檢索字符串的方式,當存在DOCTYPE
的時候,就采用DTD驗證模式,否則采用XSD
驗證模式
namespaceAware
:一個布爾值,默認為false
,在前面可以看到,在使用XSD
驗證的時候會有xmlns=""
,其實就是XML namespace
的縮寫,可以有多個命名空間,如果使用的是XSD
解析,將會把這個值改為true
解析並注冊BeanDefinitions
在上一步得到Docment
對象之后,調用registerBeanDefinitions(Document doc,Resource resource)
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//創建對象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//記錄當前已經加載的數量
int countBefore = getRegistry().getBeanDefinitionCount();
//加載並注冊
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次加載的個數
return getRegistry().getBeanDefinitionCount() - countBefore;
}
而在調用documentReader
對象方法中,才開始進行正式的解析工作
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}
解析的工作全權交給doRegisterBeanDefinition(root)
方法實現,這樣XML
文件就正式進入了解析步驟,至於怎么解析的,博主將慢慢學習並寫入后續博客.