Spring 源碼第二彈!XML 文件解析流程
spring 源碼繼續(xù)開整!
上篇文章中,松哥和大家分享了 spring 中配置文件的加載方式,如果小伙伴們還沒看過,一定先看一下,這有助于更好的理解本文,傳送門:Spring 源碼第一篇開整!配置文件是怎么加載的?。
上篇文章和大家分享了 Spring 中是如何加載本地配置文件的,如何將加載到的本地配置文件通過一個 InputStream 返回。了解到這一點之后,接下來就是對 InputStream 的解析了。
本文我們就來看一下整個解析流程是什么樣子的。
1.XmlBeanDefinitionReader
在上篇文章中,小伙伴們可以看到,XmlBeanFactory 中加載 XML 文件流的對象是 XmlBeanDefinitionReader,因此關于 XML 的解析我們就從 XmlBeanDefinitionReader 開始講起。
先來看一張 XmlBeanDefinitionReader 的繼承關系圖:
這張繼承關系圖中涉及到了幾個接口,我這里和大家說一下:
BeanDefinitionReader:這個接口主要定義了資源文件的讀取并將資源轉為 BeanDefinition。
EnvironmentCapable:這個接口定義了獲取 Environment 的方法。
AbstractBeanDefinitionReader:實現(xiàn)了 BeanDefinitionReader 和 EnvironmentCapable 接口中所定義的方法。同時,AbstractBeanDefinitionReader 中多了一個比較關鍵的屬性叫做 ResourceLoader,ResourceLoader 可以根據給定的資源返回對應的 Resource。
XmlBeanDefinitionReader 則在 AbstractBeanDefinitionReader 的基礎上繼續(xù)擴展了它的功能。
這是 XmlBeanDefinitionReader 的繼承關系。
打開 XmlBeanDefinitionReader 的源碼,我們發(fā)現(xiàn)還有兩個關鍵的對象:
BeanDefinitionDocumentReader:BeanDefinitionDocumentReader 接口只有一個實現(xiàn)類就是 DefaultBeanDefinitionDocumentReader ,在這里定義了對 Document 對象的讀取并將讀取到的屬性轉為 BeanDefinition。
DocumentLoader:將資源文件轉為 Document 對象。
擔心有的小伙伴可能不知道 Document 是啥,我這里再稍微說兩句。Document 就是 XML 解析時獲取到的文檔對象,Document 對象代表了一個 XML 文檔的模型樹,所有的其他 Node 都以一定的順序包含在 Document 對象之內,排列成一個樹狀結構,以后對 XML 文檔的所有操作都與解析器無關,直接在這個 Document 對象上進行操作即可。主流的 XML 解析方式有 SAX 解析、DOM 解析以及 Pull 解析。如果大家對于 XML 文件解析不熟悉的話,可以自行復習,松哥這里就不再啰嗦了。
好了,了解了 XmlBeanDefinitionReader 的繼承關系以及里邊定義的兩個關鍵類之后,我們來大概梳理一下 XmlBeanDefinitionReader 的功能:
首先 XmlBeanDefinitionReader 繼承自 AbstractBeanDefinitionReader,利用 AbstractBeanDefinitionReader 中的 ResourceLoader 將配置文件路徑轉為對應的 Resource。
接下來,利用 DocumentLoader 將 Resource 轉為 Document。
最后,利用 BeanDefinitionDocumentReader 去解析 Document。
把這些先搞清楚之后,接下來我們來走流程。
2.走流程
不知道還記不記得上篇文章中松哥給出的一個簡單案例:
public static void main(String[] args) { XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml")); User user = factory.getBean(User.class); System.out.println("user = " + user); }
1
2
3
4
5
我們就跟著 XmlBeanFactory 的構造方法來走一遍。
先來看 XmlBeanFactory 的構造方法:
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); } }
1
2
3
4
5
6
7
8
9
10
11
XmlBeanFactory 的源碼很簡單,其實它的主要功能都在 DefaultListableBeanFactory 中實現(xiàn)了,松哥后面會專門寫一篇文章來介紹 DefaultListableBeanFactory,這里我們先不做過多展開。
XmlBeanFactory 中定義了 XmlBeanDefinitionReader 用來讀取 Resource,默認情況下,parentBeanFactory 為 null,具體的讀取操作則是由 XmlBeanDefinitionReader#loadBeanDefinitions 方法提供的,我們來看下該方法:
@Override public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); } public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在 loadBeanDefinitions 方法中,首先會將傳入的 Resource 轉為一個 EncodedResource,也就是對傳入的資源進行編碼,所謂的編碼大家不要想的過于復雜,其實就是在將來讀取資源的時候添加一個編碼格式的參數,具體可以參見 EncodedResource#getReader 方法,因為比較簡單,我這里就不貼出來了。
繼續(xù)調用另外一個重載的 loadBeanDefinitions 方法,傳入編碼后的資源。
首先將當前資源添加到一個 ThreadLocal 中,這樣可以避免重復加載。
將 XML 配置文件的 IO 流轉為一個 InputSource 對象,InputSource 是 XML 文件解析的起點,XML 文件解析這塊大家自己復習下,松哥就不做過多介紹了。
如果資源有編碼格式,那就給 inputSource 對象也設置上編碼格式。
調用 doLoadBeanDefinitions 方法做進一步的解析操作。
最后從集合中移除資源。
在上面第 6 步的時候,調用了 doLoadBeanDefinitions 方法,這個方法要做的事情就是去將資源文件解析成 Document 對象,如下:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); return count; } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
可以看到,這里就是調用 doLoadDocument 進行資源解析,最終獲取到一個 Document 對象。
我們來看一下 doLoadDocument 方法:
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
1
2
3
4
可以看到,這里最終調用的是 documentLoader#loadDocument 方法,documentLoader 也就是松哥在第一小節(jié)和大家介紹的 DefaultDocumentLoader 對象。
該方法的調用,一共需要五個參數:
第一個 InputSource 不用多說,這是要調用的資源文件。
第二個 EntityResolver 主要是處理文件的驗證方式的。
第三個 ErrorHandler 是一個錯誤處理器。
第四個 validationMode 是指 XML 文件的驗證模式。
第五個 namespaceAware 表示是否開啟自動感知名稱空間。
具體的調用如下:
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isTraceEnabled()) { logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]"); } DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); return builder.parse(inputSource); }
1
2
3
4
5
6
7
8
9
這里我就不做過多解釋了,基本上到了 XML 解析的范疇了。小伙伴們自行復習一下 Java 中 XML 的解析方式。
3.小結
本文松哥主要和大家介紹了在 Spring 中,我們如何獲取到一個 Document 對象,拿到 Document 對象,接下來解析 Document 對象,獲取各種屬性,就能定義出 BeanDefinition 了。
但是如果大家從來沒有研究過 Spring 源碼,相信本文中可能還有很多讓你疑惑的地方,例如 EntityResolver 到底是干嘛用的?ValidationMode 又是啥?那么小伙伴們不要著急,這些東西松哥會在接下來的文章中像大家挨個介紹。
好啦,今天就先說這么多,如果大家覺得有收獲,記得點個在看鼓勵下松哥哦~
Spring XML
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。