IOC 容器的初始化過程分爲三步驟:java
- Resource 定位
- BeanDefinition 的載入和解析
- BeanDefinition 註冊
- Resource 定位。咱們通常用外部資源來描述 Bean 對象,因此在初始化 IOC 容器的第一步就是須要定位這個外部資源。
- BeanDefinition 的載入和解析。裝載就是 BeanDefinition 的載入。BeanDefinitionReader 讀取、解析 Resource 資源,也就是將用戶定義的 Bean 表示成 IOC 容器的內部數據結構:BeanDefinition。在 IOC 容器內部維護着一個 BeanDefinition Map 的數據結構,在配置文件中每個都對應着一個BeanDefinition對象。
- BeanDefinition 註冊。向IOC容器註冊在第二步解析好的 BeanDefinition,這個過程是經過 BeanDefinitionRegistery 接口來實現的。在 IOC 容器內部實際上是將第二個過程解析獲得的 BeanDefinition 注入到一個 HashMap 容器中,IOC 容器就是經過這個 HashMap 來維護這些 BeanDefinition 的。在這裏須要注意的一點是這個過程並無完成依賴注入,依賴註冊是發生在應用第一次調用 getBean() 向容器索要 Bean 時。固然咱們能夠經過設置預處理,即對某個 Bean 設置 lazyinit 屬性,那麼這個 Bean 的依賴注入就會在容器初始化的時候完成。
// 根據 Xml 配置文件建立 Resource 資源對象。ClassPathResource 是 Resource 接口的子類,
//bean.xml 文件中的內容是咱們定義的 Bean 信息。
ClassPathResource resource = new ClassPathResource("bean.xml");
//建立一個 BeanFactory。DefaultListableBeanFactory 是 BeanFactory 的一個子類,BeanFactory //做爲一個接口,其實它自己是不具備獨立使用的功能的,而 DefaultListableBeanFactory 則是真正能夠獨立
//使用的 IOC 容器,它是整個 Spring IOC 的始祖,在後續會有專門的文章來分析它。
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//建立 XmlBeanDefinitionReader 讀取器,用於載入 BeanDefinition 。
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//開啓 Bean 的載入和註冊進程,完成後的 Bean 放置在 IOC 容器中。
reader.loadBeanDefinitions(resource);
Resource 定位
- Spring 爲了解決資源定位的問題,提供了兩個接口:Resource、ResourceLoader
- 其中 Resource 接口是 Spring 統一資源的抽象接口
- ResourceLoader 則是 Spring 資源加載的統一抽象。
- 在上面那段代碼中
new ClassPathResource("bean.xml")
爲咱們定義了資源
- 那麼 ResourceLoader 則是在何時初始化的呢?
看 XmlBeanDefinitionReader 構造方法:數據結構
直接調用父類 AbstractBeanDefinitionReader :post
- 核心在於設置 resourceLoader 這段,若是設置了 ResourceLoader 則用設置的,
- 不然使用 PathMatchingResourcePatternResolver ,該類是一個集大成者的 ResourceLoader。
BeanDefinition 的載入和解析
reader.loadBeanDefinitions(resource);
開啓 BeanDefinition 的解析過程。以下:this
- 而將 Resource 封裝成 EncodedResource 主要是爲了對 Resource 進行編碼,保證內容讀取的正確性
- 從 encodedResource 源中獲取 xml 的解析源,調用
doLoadBeanDefinitions()
執行具體的解析過程
- 在該方法中主要作兩件事:
- 一、根據 xml 解析源獲取相應的 Document 對象,
- 二、調用
registerBeanDefinitions()
開啓 BeanDefinition 的解析註冊過程。
轉換爲 Document 對象
- 調用
doLoadDocument()
會將 Bean 定義的資源轉換爲 Document 對象。
- 就已經將定義的 Bean 資源文件,載入並轉換爲 Document 對象了,那麼下一步就是如何將其解析爲 Spring IOC 管理的 Bean 對象並將其註冊到容器中。
- 這個過程有方法
registerBeanDefinitions()
實現。以下:
- 首先建立 BeanDefinition 的解析器 BeanDefinitionDocumentReader,
- 而後調用 documentReader.registerBeanDefinitions() 開啓解析過程,這裏使用的是委派模式,具體的實現由子類 DefaultBeanDefinitionDocumentReader 完成。
對 Document 對象的解析
- 從 Document 對象中獲取根元素 root,而後調用
doRegisterBeanDefinitions()
開啓真正的解析過程
preProcessXml()
、postProcessXml()
爲前置、後置加強處理,目前 Spring 中都是空實現,
-
parseBeanDefinitions()
是對根元素 root 的解析註冊過程
- 迭代 root 元素的全部子節點,對其進行判斷,若節點爲默認命名空間,則ID調用
parseDefaultElement()
開啓默認標籤的解析註冊過程,
- 不然調用
parseCustomElement()
開啓自定義標籤的解析註冊過程。
標籤解析
- 若定義的元素節點使用的是 Spring 默認命名空間,則調用
parseDefaultElement()
進行默認標籤解析,以下:
- 對四大標籤:import、alias、bean、beans 進行解析,其中 bean 標籤的解析爲核心工做
對於默認標籤則由 parseCustomElement()
負責解析。
註冊 BeanDefinition
- 調用 BeanDefinitionReaderUtils.registerBeanDefinition() 註冊,
- 其實這裏面也是調用 BeanDefinitionRegistry 的 registerBeanDefinition()來註冊 BeanDefinition ,
- 不過最終的實現是在 DefaultListableBeanFactory 中實現,以下:
- 最核心的部分是這句 this.beanDefinitionMap.put(beanName, beanDefinition) ,
- 因此註冊過程也不是那麼的高大上,就是利用一個 Map 的集合對象來存放,key 是 beanName,value 是 BeanDefinition。