簡單來講,IoC容器的初始化是由前面介紹的refresh()方法來啓動的,這個啓動包括BeanDefinition的Resource定位、載入和註冊。web
(1) Resource定位過程:數據結構
指的是BeanDefinition的資源定位,由ResourceLoader經過統一的Resource接口來完成,這個Resource對各類形式的BeanDefinition的使用都提供了統一接口。好比文件系統中的Bean定義信息可使用FileSystemResource進行抽象;類路徑中的Bean定義信息可使用ClassPathResource來使用等(通常常用該方式,須要把Spring配置文件放在當前項目的classpath路徑下。classpath路徑指的是當前項目的src目錄)。這個定位過程就相似容器尋找數據的過程,就好像水桶裝水要先找水。函數
(2) BeanDefinition的載入:spa
把用戶定義好的Bean表示成IoC容器內部的數據結構,而這個容器內部的數據結構就是BeanDefinition。3d
(3) 向IoC容器註冊這些BeanDefinition的過程:xml
調用BeanDefinitionRegistry接口的實現來完成,把載入過程當中解析獲得的BeanDefinition向IoC容器進行註冊,在IoC容器內部將BeanDefinition注入到一個HashMap中,IoC容器經過這個HashMap來持有這些BeanDefinition數據。對象
PS:IoC容器初始化過程不包含Bean依賴注入的實現。在Spring中,Bean定義的載入和依賴注入是兩個獨立過程,依賴注入通常發生在應用第一次經過getBean向容器索取Bean的時候。(例外:若是對某個Bean設置了lazyinit屬性,那麼這個Bean的依賴注入在IoC容器初始化時就預先完成了)。blog
當你使用更爲底層的DefaultListableBeanFactory時,首先要定義一個Resource來定位容器使用的BeanDefinition,這個Resource並不能由DefaultListableBeanFactory直接使用,Spring經過BeanDefinitionReader來對這些信息進行處理。相比之下,在更上層的ApplicationContext中,Spring提供了一系列加載不一樣Resource的讀取器的實現,如FileSystemXmlApplicationContext就提供了從文件系統載入Resource的實現、ClassPathXmlApplication提供了從Class Path載入Resource的實現,XmlWebApplicationContext提供了從web容器中載入Resource的實現。而DefaultListableBeanFactory只是一個純粹的IoC容器,須要爲它配置特定的讀取器。索引
在FileSystemXmlApplicationContext中,構造函數實現了對configuration進行處理的功能,讓全部配置在文件系統中的以XML文件方式存在的BeanDefinition都可以獲得有效處理。構造函數中經過refresh()方法來啓動IoC容器的初始化。接口
在初始化FileSystemXmlApplicationContext的過程當中,經過IoC容器的初始化的refresh來啓動整個調用,使用的IoC容器是DefaultListableBeanFactory,具體的資源載入在XmlBeanDefinitionReader讀入BeanDefinition時完成。而getResourceByPath會被子類FileSystemXmlApplicationContext實現,這個方法返回的是一個FileSystemSource對象,經過這個對象,Spring能夠進行相關的I/O操做,完成BeanDefinition的定位。
若是是其餘的ApplicationContext,則會產生其餘種類的Resource,好比ClassPathResource、ServletContextResource等。
完成資源定位以後,具體的數據尚未導入,數據導入將會在載入和解析中完成,就比如用水桶打水,如今已經找到水源了,可是尚未把水裝到桶裏。
這個載入過程至關於把定義的BeanDefinition在IoC容器中轉化成一個Spring內部表示的數據結構的過程。IoC容器對Bean的管理和依賴注入這些功能的實現是經過對其持有的BeanDefinition進行各類相關操做完成的。這些BeanDefinition數據在IoC容器中經過一個HashMap來保持和維護。
在初始化FileSystemXmlApplicationContext的過程當中,經過調用refresh來啓動整個BeanDefinition的載入過程,這個初始化經過定義的XmlBeanDefinitionReader來完成。實際使用的IoC容器是DefaultListableBeanFactory,具體的Resource載入在XmlBeanDefinitionReader讀入BeanDefinition時實現。因爲Spring能夠對應不一樣形式的BeanDefinition,以XML方式定義就使用XmlBeanDefinitionReader,若是是其餘BeanDefinition方式,就須要使用其餘種類的BeanDefinitionReader來完成數據載入。
載入是在reader.loadBeanDefinition中開始進行的,這個方法是一個接口方法,具體的實如今XmlBeanDefinitionReader中。在讀取器中,須要獲得表明XML文件的Resource,由於這個Resource對象封裝了對XML文件的I/O操做,因此讀取器能夠在打開I/O流後獲得XML的文件對象,有了這個對象,再經過BeanDefinitionParserDelegate對這個XML的文檔樹進行解析。
解析並轉化爲容器內部數據結構是在registerBeanDefinition(doc, resource)中完成的。BeanDefinition的載入分紅兩部分:
1. 首先經過調用XML的解析器獲得document對象;
2. 在完成通用的XML解析之後,按照Spring的Bean規則進行解析。
按照Spring的Bean規則進行解析的過程是在documentReader中實現的,處理的結果由BeanDefinitionHolder對象持有,BeanDefinitionHolder除了持有BeanDefinition之外,還持有好比Bean的名字、別名集合等(BeanDefinitionHolder是BeanDefinition對象的封裝類,封裝了BeanDefinition,Bean的名字和別名)。
BeanDefinitionHolder的生成是經過BeanDefinitionParserDelegate對XML元素進行解析獲得的,BeanDefinitionParserDelegate這個類中包含了對各類Spring Bean定義規則的處理,好比對Bean元素怎麼處理,即處理XML文件中出現的<bean></bean>這個最多見的元素信息,在這裏會把id、name、aliase等屬性元素的值讀取出來,設置到生成的BeanDefinitionHolder中去。而對各類Bean的屬性配置等比較複雜的解析就經過parseBeanDefinitionElement來完成,並把解析結果設置到BeanDefinitionHolder中。在BeanDefinitionParserDelegate類中,一層一層地對BeanDefinition中的定義進行解析,好比從屬性元素集合到具體的每個屬性元素,再對具體的屬性值的處理,把解析結果封裝成PropertyValue對象並設置到BeanDefinition對象中去。
總結:通過這樣的逐層解析,XML中定義的BeanDefinition就被整個載入到了IoC容器中,並在容器中創建了數據映射。在IoC容器中創建了對應的數據結構,或者說能夠當作是POJO對象在IoC容器中的抽象,這些數據結構能夠以AbstractBeanDefinition爲入口,讓IoC容器執行索引、查詢和操做。通過以上的載入過程,IoC容器大體完成了管理Bean對象的數據準備工做(初始化),但依賴注入在這個時候尚未發生,如今,在IoC容器BeanDefinition中存在的只是一些靜態的配置信息。
上面所說的過程比較複雜,能夠結合如下這個流程圖,簡單串一下主要的方法。從總體範圍來看,載入過程的代碼是一步步細化的過程,從web.xml讀取的多是多個路徑,一個路徑下面可能有多個文件(Resource),一個文件裏面有多個Bean,一個Bean裏面有多個Property,一個Property裏面只能有一個Ref、Value和List。
通過載入和解析的過程,用戶定義的BeanDefinition信息已經在容器內創建了數據結構,可是這些數據還不能被供容器直接使用,須要在IoC容器中對這些BeanDefinition數據進行註冊。那麼註冊是在哪裏發起的?以及註冊的實現類?
在DefaultBeanDefinitionDocumentReader類的processBeanDefinition方法中,其中一個是去完成BeanDefinition的載入,另外一個是完成註冊。在DefaultListableBeanFactory中實現了BeanDefinitionRegistry的接口,這個接口的實現完成了註冊,把解析好的BeanDefinition設置到hashMap中。
完成了註冊以後就完成了IoC容器的初始化過程。