Spring源碼1 配置文件的讀取

本文的內容是讀郝佳的《Spring源碼深度解析》整理的筆記。

入門實例

書中通過一個最簡單入門實例,對配置文件的加載、配置文件中標籤的解析、bean的加載部分的源碼進行了剖析。書中的例子是基於XmlBeanFactory來創建BeanFactory,但是這個在類在Spring5.0.x中已經過時,這裏對該實例稍作改動,基於Spring5.0.x實現。

  • 首先創建一個Maven項目,pom.xml導入spring-beans.
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.15.RELEASE</version>
</dependency>
  • 創建MyBean
public class MyBean {
    private String testStr= "testStr";
    public String getTestStr() {
        return testStr;
    }
    public void setTestStr(String testStr) {
        this.testStr = testStr;
    }
}
  • 創建spring-config.xml
<?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">

    <bean id="myTestBean" class="MyBean" scope="prototype"/>

</beans>
  • 測試類
public class TestMyBean {
    @Test
    public void testSimpleLoad(){
        Resource resource = new ClassPathResource("spring-config.xml");
        BeanFactory bf = new DefaultListableBeanFactory();
        BeanDefinitionReader bdr = new XmlBeanDefinitionReader((BeanDefinitionRegistry) bf);
        bdr.loadBeanDefinitions(resource);
        MyBean myBean = (MyBean) bf.getBean("myTestBean");
        System.out.println(myBean.getTestStr());
    }
}
  • 結果

控制檯打印了testStr

上面代碼做了哪些事:

  1. 讀取spring-config.xml配置文件。
  2. 創建生產bean的工廠類BeanFactory
  3. BeanFactory根據配置文件中內容創建bean。

讀取spring-config.xml配置文件

Resource類

配置文件的作用是記錄bean信息,以及創建bean時的一些邏輯。所以Spring必然會有一些代碼是用來處理配置文件的。因爲配置文件的來源可能有很多種方式,例如:引用資源(在web項目resource根目錄)classpath:spring-config.xml(類的加載路徑)file:C:/src/spring-config.xml(文件路徑)等。

Spring將配置文件的一些屬性封裝爲Resource,包括:文件是否存在、是否可讀、是否是Open狀態、是否是文件;以及轉化爲URL、URI、File的方法;還有一些文件屬性的獲取。

URL、URI、File分別是Java中對url、uri、文件對應的對象。(Java將不同來源的資源抽象成URL,通過註冊不同的URLStreamHandler來處理不同來源的資源讀取邏輯,handler類型的選擇根據不同的前綴(協議,Protocol)來識別)

Resource源碼:(點擊放大)

InputStreamSource源碼:(點擊放大)上面Resource類繼承了InputStreamSource接口,InputStreamSource源碼如下:

可以通過下面這種方式獲取InputStream對象

Resource resource = new ClassPathResource("spring-config.xml");
InputStream in = resource.getInputStream();

Document類

獲取到配置資源後,Spring將讀取配置文件,並將spring-config.xml文件中的內容封裝爲Document類。在前面的實例中,這個過程在bdr.loadBeanDefinitions(resource);方法中體現。

深入該方法後,走到XmlBeanDefinitionReader類,loadBeanDefinitions(Resource resource)方法如下。

可以看到loadBeanDefinitions中調用了該類下的同名方法,調用方法前將Resource封裝爲EncodedResource類,可以推斷這個方法是用來給文件做編碼處理的,該類中的主要邏輯體現在getReader()方法,當設置了編碼屬性的時候Spring會使用相應的編碼作爲輸入流的編碼。

loadBeanDefinitions(EncodedResource encodedResource)方法細節:(點擊放大)

該方法中最終返回的是doLoadBeanDefinitions(inputSource, encodedResource.getResource())執行的返回結果,doLoadBeanDefinitions方法中傳入兩個參數,其中第一個參數inputSource是Resource對應的InputStream封裝得到,目的是想要通過SAX讀取XML文件的方式來準備InputSource對象。真正處理的方法是doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法。

doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法細節:(點擊放大)

到了這個方法後,終於見到了Document類。繼續跟蹤doLoadDocument方法。

下面分別解釋this.documentLoader.loadDocument方法中的參數:

  • inputSource
  • getEntityResolver():得到一個解析器(org.xml.sax.EntityResolver)。(官網解釋:如果SAX應用程序需要實現自定義處理外部實體,則必須實現此接口,並使用setEntityResolver方法向SAX 驅動器註冊一個實例.也就是說,對於解析一個xml,sax首先會讀取該xml文檔上的聲明,根據聲明去尋找相應的dtd定義,以便對文檔的進行驗證,默認的尋找規則,(即:通過網絡,實現上就是聲明DTD的地址URI地址來下載DTD聲明),並進行認證,下載的過程是一個漫長的過程,而且當網絡不可用時,這裏會報錯,就是應爲相應的dtd沒找到。)該方法的存在就是提供了一個尋找DTD的方法,例如,可以指定本地,不必從網絡加載。
  • this.errorHandler:xml錯誤處理。
  • getValidationModeForResource(resource):獲取xml文件的驗證模式。
  • isNamespaceAware():如果沒有驗證文件的話,namespaceAware必須設置而爲true.(Set whether or not the XML parser should be XML namespace aware. Default is “false”. This is typically not needed when schema validation is active. However, without validation, this has to be switched to “true” in order to properly process schema namespaces.)

this.documentLoader.loadDocument方法主要做的就是將xml文件封裝爲Document對象,細節都是解析的過程。

下面回到doLoadDocument這裏,繼續看registerBeanDefinitions方法:該方法主要用來提取bean和註冊bean。

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

淺談BeanDefinition註冊

registerBeanDefinitions(doc, resource)方法:
在這裏插入圖片描述
同名方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
在這裏插入圖片描述
繼續跟蹤doRegisterBeanDefinitions(root)方法:
在這裏插入圖片描述
preProcessXml(root)postProcessXml(root)方法是空方法,這兩個方法是爲子類設計的,這是模板設計模式,如果繼承DefaultBeanDefinitionDocumentReader的子類需要在Bean解析前後做一些處理的話,那麼只需要重寫這兩個方法就可以了。

處理完profile標籤以後就要進行xml文件的讀取了,進入parseBeanDefinitions(root, this.delegate)方法中:
在這裏插入圖片描述

Spring的xml配置裏面有兩大類Bean聲明,一個是默認標籤,例如:<bean id="myTestBean" class="MyBean" scope="prototype"/>,另外一種是自定義標籤,例如:<tx:annotation-driven />

在介紹Spring默認標籤解析之前,先介紹一下Spring中常用的一些標籤。後面再介紹關於這些標籤的解析。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章