從源碼看Spring中IOC容器的實現(二):IOC容器的初始化

初始化的三個主要過程

Spring中IOC容器的初始化是由refresh()方法來啓動的,該方法標誌着IOC容器的正式啓動。具體來講,這個啓動包括BeanDefinition的Resource定位、載入和註冊三個基本過程。數據結構

注意:這裏的初始化不包含Bean依賴注入的實現dom

Spring將上述三個過程分離,使用相應的ResourceLoader、BeanDefinitionReader等模塊,這樣設計可讓用戶更加靈活地對這三個過程進行剪裁或擴展,函數

  1. Resoure定位:即BeanDefinition的資源定位,由ResourceLoader經過統一的Resource接口來完成,Resource對各類形式的BeanDefinition的使用都提供了統一接口。
  2. BeanDefinition的載入:這個載入過程是把用戶定義好的Bean表示成IOC容器內部的數據結構,即BeanDefinition。
  3. BeanDefinition的註冊:經過調用BeanDefinitionRegistry接口的實現來完成。這個註冊過程把載入過程當中解析獲得的BeanDefinition向IOC容器中註冊,實際是注入到一個HashMap中。

BeanDefinition:

上一篇文章咱們介紹了Spring提供的基本IOC容器的接口定義和實現.在這些Spring提供的基本IOC容器的接口定義和實現的基礎上,Spring經過定義BeanDefinition來管理基於Spring的應用中的各類對象以及他們之間的相互依賴關係。設計

BeanDefinition抽象了咱們對Bean的定義,是讓容器起做用的主要數據類型。對IOC容器來講,BeanDefinition就是對依賴反轉模式中管理的對象依賴關係的數據抽象,也是容器實現依賴反轉功能的核心數據結構,依賴反轉功能都是圍繞對這個BeanDefinition的處理來完成的。3d

Resource定位

以FileSystemXmlApplicationContext爲例。其經過getResourceByPath方法加載xml,返回一個Resource。這裏使用了模板方法模式。cdn

在FileResourceLoader中能夠找到一樣的方法,而該方法來自於FileResourceLoader重寫其父類DefaultResourceLoader,該方法在ResourceLoader接口中定義,在FileResourceLoader和DefaultResourceLoader中都提供了各自的實現。而其調用在DefaultResourceLoader的getResource方法中:xml

這裏確實是模板方法模式,子類覆蓋父類實現。而經過查找該方法的調用,首先是看FileSystemXmlApplicationContext的loadBeanDefinitions方法,該方法在AbstractXmlApplicationContext中實現:對象

此時已經在AbstractXmlApplicationContext中注入了對應的BeanDefinitionReader,再調用reader的loadBeanDefinitions方法,追進去看AbstractBeanDefinitionReader類,在這裏實現了:blog

這裏能夠看出在BeanDefinitionReader中注入了一個ResourceLoader,此處能夠看到對ResourceLoader調用了getResource方法,這樣就能夠完成對該方法的整個追溯過程了索引

要注意到FileSystemXmlApplicationContext自己就是一個DefaultResourceLoader的子類,其實FileSystemXmlApplicationContext中的getResource就是給父類做爲模板方法調用。

那麼FileSystemXmlApplicationContext是在什麼地方定義了BeanDefinition的讀入器BeanDefinitionReader並完成BeanDefinition的讀入呢?

BeanDefinition的載入

先回到IOC容器的初始化入口,也就是看一下refresh方法,這個方法的最初是在FileSystemXmlApplicationContext的構造函數中被調用的,它的調用標誌着容器初始化的開始,這些初始化對象就是BeanDefinition數據。

對容器的啓動來講,refresh是一個很重要的方法。該方法在AbstractApplicationContext類中找到,它詳細的描述了整個ApplicationContext的初始化過程。

咱們去看FileSystemXmlApplicationContext的基類AbstractRefreshableApplicationContext是怎樣實現的。 首先看他的refreshBeanFactory方法:

首先建立其須要注入的DefaultListableBeanFactory,接着調用loadBeanDefinitions來載入資源進入其所持有的BeanFactory,而這個方法在該類中是一個空實現,留給子類去作對應的實現。 而在AbstractXMLApplicationContext中,咱們找到了對應的具體實現:

這裏能夠清楚的看到讀入器的建立和配置,這裏AbstractXMLApplicationContext將DefaultListableBeanFactory做爲參數注入了BeanDefinitionReader,但注意,其注入是以BeanDefinitionRegistry接口的形式:

父類AbstractBeanDefinitionReader構造器:

若是傳入的BeanFactory只實現了BeanDefinitionRegistry,那就注入,若是還實現了ResourceLoader,就把它做爲默認的ResourceLoader,若是實現了EnvironmentCapable,會把其內部的環境也加載進來。 而後咱們回過來繼續看其子類XMLBeanDefinitionReader,其重載了loadBeanDefinitions方法,不使用父類的實現

這裏調用了doLoadBeanDefinition方法:

該方法一共就兩步,一步是加載dom,另外一步是從dom將BeanDefinition註冊到BeanFactory中。

在註冊的方法中,是經過調用BeanDefinitionDocumentReader進行的,實際調用的是其實現類DefaultBeanDefinitionDocumentReader,在其方法中又是經過其持有的BeanDefinitionParserDelegate進行的。

總結一下流程:能夠看到,在初始化FileSystemXMLApplicationContext的過程當中是經過調用IOC容器的refresh來啓動整個BeanDefinition的載入過程的,這個初始化是經過定義的XMLBeanDefinitionReader來完成。同時,咱們也知道實際使用的IOC容器是DefaultListableBeanFactory,具體的Resource載入在XMLBeanDefinitionReader讀入BeanDefinition時實現。在XMLBeanDefinitionReader的實現中能夠看到,是在reader.loadBeanDefinition中開始進行BeanDefinition的載入,而這時其父類AbstractBeanDefinitionReader已經爲BeanDefinition的載入作好了準備。

這裏調用的是loadBeanDefinitions(Resource res)方法,但這個方法在該類中是沒有實現的,具體實如今XMLBeanDefinitionReader中。在讀取器中,須要獲得表明 xml文件的Resource,由於這個Resource對象封裝了對xml文件的I/O操做,因此讀取器能夠在打開IO流後獲得xml的文件對象,有了這個文件對象後,就能夠按照Spring的Bean定義規則來對這個xml的文檔樹進行解析。

BeanDefinition的載入分紅兩部分,首先經過調用xml的解析器獲得document對象,但這些對象並無按照Spring的Bean規則進行解析。在完成通用的xml解析之後,纔是按照Spring的Bean規則進行解析的地方,這個按照Spring的Bean規則進行解析的過程是在DocumentReader中實現的。這裏使用的是默認的DefaultBeanDefinitionDocumentReader。這個DefaultBeanDefinitionDocumentReader的建立是在後面的方法中完成的,而後再完成BeanDefinition的處理,處理的結果由BeanDefinitionHolder對象來持有。這個BeanDefinitionHolder除了持有BeanDefinition對象外,還持有其餘與BeanDefinition的使用相關的信息,好比Bean的名字、別名集合等。這個BeanDefinitionHolder的生成是經過對Document文檔樹的內容解析來完成的,能夠看到這個解析過程是由BeanDefinitionParserDelegate來實現(具體在上面的processBeanDefinition方法中)。

解析過程: 調用delegate的parseBeanDefinitionElement方法:

在高亮的這一行進行對bean元素的詳細解析:

再往裏面就是各類元素的解析了,太細了之後慢慢看。

通過這樣逐層的解析,咱們在xml文件中定義的BeanDefinition就被整個載入到了IOC容器中,並在容器中創建了數據映射。在IOC容器中創建了對應的數據結構,或者說能夠當作是pojo對象在IOC容器中的抽象,這些數據結構能夠以AbstractBeanDefinition爲入口,讓IOC容器執行索引、查詢和操做。通過以上過程,IOC容器大體完成了管理Bean對象的數據準備工做。可是,重要的依賴注入實際上在這個時候還沒發生。如今IOC容器BeanDefinition中存在的還只是一些靜態的配置信息。嚴格的說這時候容器尚未徹底起做用,要徹底發揮容器的做用,還需完成數據向容器的註冊。

BeanDefinition的註冊

在BeanDefinition的載入和解析完成後,用戶定義的BeanDefinition信息已經在IOC容器內創建了本身的數據結構以及相應的數據表示,但此時這些數據還不能供IOC容器直接使用,須要在IOC中對這些BeanDefinition數據進行註冊。在DefaultListableBeanFactory中,是經過一個HashMap來持有載入的BeanDefinition的,這個HashMap的定義在DefaultListableBeanFactory中能夠看到。

將解析獲得的BeanDefinition向IOC容器中的map註冊的過程是在載入BeanDefinition完成後進行的,調用過程以下:

具體實現: DefaultBeanDefinitionDocumentReader中:

BeanDefinitionReaderUtils中:

調用了注入的registry(即DefaultListableBeanFactory): 看DefaultListableBeanFactory中的registerBeanDefinition方法:

到這裏註冊就已經完成了。

相關文章
相關標籤/搜索