IOC 容器的初始化過程分爲三步驟:

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 :

核心在於設置 resourceLoader 這段,如果設置了 ResourceLoader 則用設置的,否則使用 PathMatchingResourcePatternResolver ,該類是一個集大成者的 ResourceLoader。

BeanDefinition 的載入和解析

reader.loadBeanDefinitions(resource); 開啓 BeanDefinition 的解析過程。如下:

而將 Resource 封裝成 EncodedResource 主要是爲了對 Resource 進行編碼,保證內容讀取的正確性

從 encodedResource 源中獲取 xml 的解析源,調用 doLoadBeanDefinitions() 執行具體的解析過程在該方法中主要做兩件事:1、根據 xml 解析源獲取相應的 Document 對象,2、調用 registerBeanDefinitions() 開啓 BeanDefinition 的解析註冊過程。

轉換爲 Document 對象

調用 doLoadDocument() 會將 Bean 定義的資源轉換爲 Document 對象。loadDocument() 方法接受五個參數:inputSource:加載 Document 的 Resource 源entityResolver:解析文件的解析器errorHandler:處理加載 Document 對象的過程的錯誤validationMode:驗證模式namespaceAware:命名空間支持。如果要提供對 XML 名稱空間的支持,則爲true

就已經將定義的 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。

查看原文 >>
相關文章