1. 程式人生 > >《Spring技術內幕》學習筆記3——IoC容器載入Bean定義資原始檔

《Spring技術內幕》學習筆記3——IoC容器載入Bean定義資原始檔

1.SpringIoC容器將Bean定義的資原始檔封裝為SpringResource之後,接下來要做的就是通過Spring的資源載入器(resourceLoader)讀入Bean定義資原始檔的過程。對於IoC容器來說,Bean定義的載入過程就是將Bean定義資原始檔讀入進記憶體並解析轉換成Spring所管理的Bean的資料結構的過程。相對於SpringIoC容器定位Bean定義資原始檔來說,Bean定義資原始檔的載入和解析過程更復雜一些,因此按照程式的執行步驟逐條分析其實現原理。

首先從FileSystemXmlApplicationContext的入口建構函式分析起其程式碼如下

 

Spring IoC容器對Bean定義資源的載入是從refresh()函式開始的,FileSystemXmlApplicationContext通過呼叫其父類AbstractApplicationContextrefresh()函式啟動整個IoC容器對Bean定義的載入過程。

3.AbstractApplicationContextrefresh函式載入Bean定義過程:

 

refresh()方法主要為IoC容器Bean的生命週期管理提供條件,Spring IoC容器載入Bean定義資原始檔從其子類容器的refreshBeanFactory()方法啟動,所以整個refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”這句以後程式碼的都是註冊容器的資訊源和生命週期事件,載入過程就是從這句程式碼啟動。

AbstractApplicationContextobtainFreshBeanFactory()方法呼叫子類容器的refreshBeanFactory()

方法,啟動容器載入Bean定義資原始檔的過程,程式碼如下:

 

4.AbstractApplicationContext子類的refreshBeanFactory()方法:

AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正呼叫的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法,方法的原始碼如下:

 

refresh()方法的作用是:在建立IoC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IoC容器。refresh的作用類似於對IoC容器的重啟,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入。

refreshBeanFactory方法類似,載入Bean定義的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext類中只定義了抽象方法,具體的實現呼叫子類容器中的方法實現。

AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正呼叫的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要原始碼如下:

 

Xml Bean讀取器(XmlBeanDefinitionReader)呼叫其父類AbstractBeanDefinitionReader reader.loadBeanDefinitions方法讀取Bean定義資源。

由於我們使用FileSystemXmlApplicationContext作為例子分析因此getConfigResources的返回值為null,因此程式執行reader.loadBeanDefinitions(configLocations)分支。

 

loadBeanDefinitions(Resource...resources)方法和上面分析的3個方法類似,同樣也是呼叫XmlBeanDefinitionReaderloadBeanDefinitions方法。

從對AbstractBeanDefinitionReaderloadBeanDefinitions方法原始碼分析可以看出該方法做了以下兩件事:

首先,呼叫資源載入器的獲取資源方法resourceLoader.getResource(location)獲取到要載入的資源。

其次,真正執行載入功能是其子類XmlBeanDefinitionReaderloadBeanDefinitions方法

7.資源載入器獲取要讀入的資源:

XmlBeanDefinitionReader通過呼叫其父類DefaultResourceLoadergetResource方法獲取要載入的資源,其原始碼如下:

 

FileSystemXmlApplicationContext容器提供了getResourceByPath方法的實現,就是為了處理既不是classpath標識,又不是URL標識的Resource定位這種情況。

現在,Bean定義的Resource得到了,下面我們繼續跟隨程式執行方向,分析XmlBeanDefinitionReaderloadBeanDefinitions方法。

 

通過原始碼分析,載入Bean定義資原始檔的最後一步是將Bean定義資源轉換為Document物件,該過程由documentLoader實現。

9. DocumentLoaderBean定義資源轉換為Document物件:

DocumentLoaderBean定義資源轉換成Document物件的原始碼如下:

 

該解析過程呼叫JavaEE標準的JAXP標準進行處理。

至此Spring IoC容器根據定位的Bean定義資原始檔,將其載入讀入並轉換成為Document物件過程完成。

10.看原始碼的個人心得總結:

通過這幾天看原始碼,個人總結一些心得:程式碼畢竟不是文章,不能從頭到尾詳細看,個人覺得看原始碼比較好的方法是:

首先,先實現一個簡單例子呼叫原始碼,讓整個應用能簡單跑起來。

然後,在進入程式碼最開始的地方打一個除錯斷點,使用Debug工具進行單步除錯,直到跑完所有流程。

通過除錯就可以理解整個程式碼的工作流程和呼叫順序,有助於理清思路,理解其大概的設計思想。

Spring程式碼的確實比較複雜,程式碼中大量使用了設計模式,另外為了解耦合,程式碼的分工比較明確,物件也非常的多,對於沒有分析原始碼經驗的人來說,閱讀程式碼發現其跨度和跳轉非常大,難度和挑戰比較大。

我也是第一次分析Spring原始碼,希望和大家一起學習探討。