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擷取輸入流
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jNyQzMxATM5AzNxkDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
這裡可以看到,對得到的輸入流inputStream 先封裝成一個byte stream,然後調用doLoadBeanDefinitions()方法,從它的注釋可以看到doLoadBeanDefinitions才是真正加載bean的方法,前邊的都是一些邏輯準備。下邊看看doLoadBeanDefinitions具體都做了哪些事情
doLoadBeanDefinitions主要做了兩件事,1.通過doLoadDocument()方法得到輸入流的Document文檔對象,2.根據Document資訊注冊bean。再來深入看看doLoadDocument()都幹了什麼事情。
在這個方法裡面真正構造了輸入流得Document對象,而getValidationModeForResource()是用來得到xml檔案的驗證模式,dtd或者xsd,用來對文檔的完整性進行驗證,若通過驗證,就構造了Document,再看看loadDocument()裡面的實作
這裡是通過建立DocumentBuilderFactory,進而建立DocumentBuilder,然後通過解析inputSource來傳回Document文檔。這裡的EntityResolver對象是用來提供一個尋找dtd聲明的方法,因為通過我們擷取dtd驗證模式都是通過網絡,當網絡要是出現變慢或者不可用的情況時,就會報錯,是以這裡我們手動指定去哪裡找驗證檔案,就會避免通過網絡來尋找文檔的麻煩。
3.提取注冊bean
當檔案轉換為Document後,下來就是提取和注冊bean了
第一行,建立BeanDefinitionDocumentReader,用來後續讀取Document文檔内容 第二行,設定環境變量 第三行,擷取加載前BeanDefinition的加載個數 第四行,加載注冊bean 第五行,擷取本次加載的BeanDefinition個數 核心在第四行registerBeanDefinitions(),我們看看它的代碼
可以看到,在registerBeanDefinitions()裡面,擷取了Document的根節點root,它是用來後邊解析xml文檔用的,然後就是真正的處理方法doRegisterBeanDefinitions()了,我們看看它的實作。
先來看這個方法的注釋,使用給定的root來一個個注冊bean,這裡面有三個方法preProcessXml()、parseBeanDefinitions()、postProcessXml(),前後個是留給子類使用的,這裡沒有做實作,如果需要在注冊前後做一些處理的話,就寫在這兩個方法裡面。這裡有個profile,它是用來配置環境的,如生産環境和開發環境,如果定義了profile,就回去環境變量裡面去找,環境變量可能有多個,是以這裡要對結果進行拆分,并對每個環境變量去進行解析acceptsProfiles(),如果有不符合的,則不進行下邊的解析工作。下面來看看具體的解析工作
這裡看到,對根節點和每個子節點判斷是否預設的命名空間,如果是,則調用parseDefaultElement進行解析,如果是自定義命名空間,則調用parseCustomElement進行解析。我們知道,spring裡面有兩大類的bean聲明,一種是預設的,<bean id="***" class="***"/> ,另外一種是自定義的,像事務的聲明,<tx:annotation-driven> 當parseBeanDefinitions方法執行完畢之後,spring中的bean就真正由配置檔案的存在進入到記憶體中,可以随時供我們使用了。 總結起來,spring配置檔案的加載,先是由檔案路徑生成Resource的輸入流,然後由輸入流生成Document文檔,然後解析Document文檔,真正将bean執行個體化進記憶體,當然這個過程中,有好多的驗證,轉換過程,我的解釋也隻是皮毛而已,沒有面面俱到!學習筆記,不喜勿噴,歡迎交流。 下篇說說,我們的Document具體是怎麼解析并注冊到記憶體中的。