天天看點

spring加載--從xml配置檔案到記憶體

   spring最初的形态是存在于xml配置檔案中的,使用的時候bean執行個體又是存在于記憶體中的,今兒聊聊spring對于xml檔案的加載,也就是spring怎麼将bean從xml搬到記憶體中的。

1.spring管理使用bean無非就是這幾步

     第一步:讀取配置檔案***.xml。

     第二步:根據配置檔案找到對應的bean配置,并将其執行個體化。

     第三步:調用執行個體化後的執行個體。

2.先來看讀取配置檔案

     通常情況下我們通過BeanFactory f=new XmlBeanFactory(new ClassPathResource("*****.xml")來讀取配置檔案。

     可以看到,先是通過調用ClassPathResource來構造Resource資源檔案的執行個體對象,構造完了之後,就可以通過Resource提供的服務來進行操作了。

  1.通過Resource類對配置檔案進行封裝

     首先,ClassPathResource繼承了InputStreamSource類,它隻有一個方法getInputStream(),可以傳回任何能封裝InputStream的類,如File、classpath下的資源。ClassPathResource執行的時候,先判斷目前路徑下資源的存在性、可讀性、是否處于打開狀态,以及擷取其他的資源屬性。當ClassPathResource執行完畢之後,我們就會得到一個Resource對象,這就是一個輸入流,有了它,便可以對所有資源檔案進行統一處理了。

  2.對封裝的資源檔案進行讀取

        public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {

super(parentBeanFactory);

this.reader.loadBeanDefinitions(resource);

}

       看看這個構造方法,這裡就是通過loadBeanDefinitions來對生成的Resource資源進行加載的

       public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {

return loadBeanDefinitions(new EncodedResource(resource));

}

      可以看到,loadBeanDefinitions做的第一件事就是通過EncodedResource對Resource進行封裝,主要作用是對于資源檔案的編碼進行處理。

      InputStream inputStream = encodedResource.getResource().getInputStream();  然後在loadBeanDefinitions擷取輸入流

spring加載--從xml配置檔案到記憶體
spring加載--從xml配置檔案到記憶體

         這裡可以看到,對得到的輸入流inputStream 先封裝成一個byte stream,然後調用doLoadBeanDefinitions()方法,從它的注釋可以看到doLoadBeanDefinitions才是真正加載bean的方法,前邊的都是一些邏輯準備。下邊看看doLoadBeanDefinitions具體都做了哪些事情

spring加載--從xml配置檔案到記憶體

          doLoadBeanDefinitions主要做了兩件事,1.通過doLoadDocument()方法得到輸入流的Document文檔對象,2.根據Document資訊注冊bean。再來深入看看doLoadDocument()都幹了什麼事情。

spring加載--從xml配置檔案到記憶體

      在這個方法裡面真正構造了輸入流得Document對象,而getValidationModeForResource()是用來得到xml檔案的驗證模式,dtd或者xsd,用來對文檔的完整性進行驗證,若通過驗證,就構造了Document,再看看loadDocument()裡面的實作

spring加載--從xml配置檔案到記憶體

    這裡是通過建立DocumentBuilderFactory,進而建立DocumentBuilder,然後通過解析inputSource來傳回Document文檔。這裡的EntityResolver對象是用來提供一個尋找dtd聲明的方法,因為通過我們擷取dtd驗證模式都是通過網絡,當網絡要是出現變慢或者不可用的情況時,就會報錯,是以這裡我們手動指定去哪裡找驗證檔案,就會避免通過網絡來尋找文檔的麻煩。

3.提取注冊bean

     當檔案轉換為Document後,下來就是提取和注冊bean了

spring加載--從xml配置檔案到記憶體

    第一行,建立BeanDefinitionDocumentReader,用來後續讀取Document文檔内容     第二行,設定環境變量     第三行,擷取加載前BeanDefinition的加載個數     第四行,加載注冊bean     第五行,擷取本次加載的BeanDefinition個數      核心在第四行registerBeanDefinitions(),我們看看它的代碼

spring加載--從xml配置檔案到記憶體

    可以看到,在registerBeanDefinitions()裡面,擷取了Document的根節點root,它是用來後邊解析xml文檔用的,然後就是真正的處理方法doRegisterBeanDefinitions()了,我們看看它的實作。

spring加載--從xml配置檔案到記憶體

     先來看這個方法的注釋,使用給定的root來一個個注冊bean,這裡面有三個方法preProcessXml()、parseBeanDefinitions()、postProcessXml(),前後個是留給子類使用的,這裡沒有做實作,如果需要在注冊前後做一些處理的話,就寫在這兩個方法裡面。這裡有個profile,它是用來配置環境的,如生産環境和開發環境,如果定義了profile,就回去環境變量裡面去找,環境變量可能有多個,是以這裡要對結果進行拆分,并對每個環境變量去進行解析acceptsProfiles(),如果有不符合的,則不進行下邊的解析工作。下面來看看具體的解析工作

spring加載--從xml配置檔案到記憶體

      這裡看到,對根節點和每個子節點判斷是否預設的命名空間,如果是,則調用parseDefaultElement進行解析,如果是自定義命名空間,則調用parseCustomElement進行解析。我們知道,spring裡面有兩大類的bean聲明,一種是預設的,<bean    id="***"     class="***"/> ,另外一種是自定義的,像事務的聲明,<tx:annotation-driven>      當parseBeanDefinitions方法執行完畢之後,spring中的bean就真正由配置檔案的存在進入到記憶體中,可以随時供我們使用了。       總結起來,spring配置檔案的加載,先是由檔案路徑生成Resource的輸入流,然後由輸入流生成Document文檔,然後解析Document文檔,真正将bean執行個體化進記憶體,當然這個過程中,有好多的驗證,轉換過程,我的解釋也隻是皮毛而已,沒有面面俱到!學習筆記,不喜勿噴,歡迎交流。       下篇說說,我們的Document具體是怎麼解析并注冊到記憶體中的。