IOC容器的初始化(一)

IOC 容器的初始化包括 BeanDefinition 的 Resource 定位、載入和註冊這三個基本的過程。我們以 ApplicationContext 爲例講解,ApplicationContext 系列容器也許是我們最熟悉的,因爲 Web 項 目 中 使 用 的 XmlWebApplicationContext 就 屬 於 這 個 繼 承 體系 , 還 有ClasspathXmlApplicationContext 等,其繼承體系如下圖所示:


ApplicationContext 允許上下文嵌套,通過保持父上下文可以維持一個上下文體系。對於 Bean 的查找可以在這個上下文體系中發生,首先檢查當前上下文,其次是父上下文,逐級向上,這樣爲不同的Spring 應用提供了一個共享的 Bean 定義環境。

下面我們分別簡單地演示一下兩種 IOC 容器的創建過程

1、FileSystemXmlApplicationContext IOC 容器流程

(1)、ApplicationContext = new FileSystemXmlApplicationContext(xmlPath);
先看其構造函數的調用:

實際調用的構造函數:


(2)、設置資源加載器和資源定位

通過分析在創建FileSystemXmlApplicationContext容器時,構造方法做以下兩項重要工作:首先,調用父類容器的構造方法(super(parent)方法)爲容器設置好Bean資源加載器。然後,再調用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設置Bean定義資源文件的定位路徑。通過追蹤FileSystemXmlApplicationContext的繼承體系,發現其父類的父類

AbstractApplicationContext中初始化IOC容器所做的主要源碼如下:



AbstractApplicationContext 構造方法中調用 PathMatchingResourcePatternResolver 的構造

方法創建 Spring 資源加載器 。


接下來FileSystemXmlApplicationContext執行setConfigLocations方法通過調用其父類AbstractRefreshableConfigApplicationContext的方法進行對Bean定義資源文件的定位,該方法的源碼如下:



通過這兩個方法的源碼我們可以看出,我們既可以使用一個字符串來配置多個 Spring Bean 定義資源文件,也可以使用字符串數組,即下面兩種方式都是可以的:
ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);多個資源文件路徑之間可以是用” , ; \t\n”等分隔。
ClasspathResource res = new ClasspathResource(newString[]{“test1.xml”,”test2.xml”,……});

至此,SpringIOC 容器在初始化時將配置的 Bean 定義資源文件定位爲 Spring 封裝的 Resource。

(3)、AbstractApplicationContext 的 refresh 函數載入 Bean 定義過程:
SpringIOC 容器對 Bean 定義資源的載入是從 refresh()函數開始的,refresh()是一個模板方法,refresh()方法的作用是:在創建 IOC 容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,
以保證在 refresh 之後使用的是新建立起來的 IOC 容器。refresh 的作用類似於對 IOC 容器的重啓,在新建立好的容器中對容器進行初始化,對 Bean 定義資源進行載入
FileSystemXmlApplicationContext 通過調用其父類 AbstractApplicationContext 的 refresh()
函數啓動整個 IOC 容器對 Bean 定義的載入過程:



refresh()方法主要爲IOC容器Bean的生命週期管理提供條件,IOC載入Bean定義資源文件從其子類容器的refreshBeanFactory()啓動,所以整個refresh()中ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();這句以後代碼的都是註冊容器的信息源和生命週期事件,載入過程就是從這句代碼啓動。refresh()方法的作用是:在創建IOC容器前,如果已經有容器存在,則需要把已有的容器銷燬和關閉,以保證在refresh之後使用的是新建立起來的IOC容器。refresh的作用類似於對IOC容器的重啓,在新建立好的容器中對容器進行初始化,對Bean定義資源進行載入

(4)、AbstractApplicationContext的obtainFreshBeanFactory()方法調用子類容器的


AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正調用的是其子類AbstractRefreshableApplicationContext實現的refreshBeanFactory()方法,

方法的源碼如下:



在這個方法中,先判斷BeanFactory是否存在,如果存在則先銷燬beans並關閉beanFactory,接着創建DefaultListableBeanFactory,並調用loadBeanDefinitions(beanFactory)裝載bean定義。

(5)、AbstractRefreshableApplicationContext子類的loadBeanDefinitions方法:
AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調用的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要源碼如下:loadBeanDefinitions方法同樣是抽象方法,是由其子類實現的,也即在AbstractXmlApplicationContext中。





XmlBean讀取器(XmlBeanDefinitionReader)調用其父類AbstractBeanDefinitionReader的reader.loadBeanDefinitions方法讀取Bean定義資源。由於我們使用FileSystemXmlApplicationContext作爲例子分析,因此getConfigResources的返回值爲null,因此程序執行reader.loadBeanDefinitions(configLocations)分支。

(6)、AbstractBeanDefinitionReader讀取Bean定義資源,在其抽象父類AbstractBeanDefinitionReader中定義了載入過程。
AbstractBeanDefinitionReader的loadBeanDefinitions方法源碼如下:




loadBeanDefinitions(Resource...resources)方法調用XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。從對 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源碼分析可以看出該方法做了以下兩件事:首先,調用資源加載器的獲取資源方法 resourceLoader.getResource(location),獲取到要加載的資源。其次,真正執行加載功能是其子類 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。



看到上面的ResourceLoader與ApplicationContext的繼承系圖,可以知道其實際調用的是DefaultResourceLoader中的getSource()方法定位Resource,因爲FileSystemXmlApplicationContext本身就是DefaultResourceLoader的實現類,所以此時又回到了FileSystemXmlApplicationContext中來。

(7)、資源加載器獲取要讀入的資源:

XmlBeanDefinitionReader通過調用其父類DefaultResourceLoader的getResource方法獲取要加載的資源,其源碼如下


如果不是classPath路徑也不是Url方式,那麼使用文件系統資源對象來定義bean文件


這樣代碼就回到了FileSystemXmlApplicationContext中來,他提供了FileSystemResource來完成從文件系統得到配置文件的資源定義。
這樣,就可以從文件系統路徑上對IOC配置文件進行加載,當然我們可以按照這個邏輯從任何地方加載,在Spring中我們看到它提供的各種資源抽象,比如

ClassPathResource,URLResource,FileSystemResource等來供我們使用。上面我們看到的是定位Resource的一個過程,而這只是加載過程的一部分.

(8)、XmlBeanDefinitionReader加載Bean定義資源:

繼續回到XmlBeanDefinitionReader的loadBeanDefinitions(Resource…)方法看到代表bean文件的資源定義以後的載入過程



通過源碼分析,載入Bean定義資源文件的最後一步是將Bean定義資源轉換爲Document對象,該過程由documentLoader實現

(9)、DocumentLoader將Bean定義資源轉換爲Document對象:

DocumentLoader將Bean定義資源轉換成Document對象的源碼如下:


該解析過程調用JavaEE標準的JAXP標準進行處理。至此SpringIOC容器根據定位的Bean定義資源文件,將其加載讀入並轉換成爲Document對象過程完成。接下來我們要繼續分析SpringIOC容器將載入的Bean定義資源文件轉換爲Document對象之

後,是如何將其解析爲SpringIOC管理的Bean對象並將其註冊到容器中的。

(10)、XmlBeanDefinitionReader解析載入的Bean定義資源文件:

XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML文件中實際載入Bean定義資源的方法,該方法在載入Bean定義資源之後將其轉換爲Document對象,接下來調用registerBeanDefinitions啓動SpringIOC容器對Bean定義的解析過程。
registerBeanDefinitions方法源碼如下:


Bean定義資源的載入解析分爲以下兩個過程:
首先,通過調用XML解析器將Bean定義資源文件轉換得到Document對象,但是這些Document對象並沒有按照Spring的Bean規則進行解析。這一步是載入的過程

其次,在完成通用的XML解析之後,按照Spring的Bean規則對Document對象進行解析。按照Spring的Bean規則對Document對象解析的過程是在接口BeanDefinitionDocumentReader的實現類DefaultBeanDefinitionDocumentReader中實現的。

(11)、DefaultBeanDefinitionDocumentReader對Bean定義的Document對象解析:
BeanDefinitionDocumentReader通過registerBeanDefinitions調用其實現類DefaultBeanDefinitionDocumentReader對Document對象進行解析,解析的代碼如下:




通過上述SpringIOC容器對載入的Bean定義Document解析可以看出,我們使用Spring時,在Spring配置文件中可以使用<import>元素來導入IOC容器所需要的其他資源,SpringIOC容器在解析時會首先將指定導入的資源加載進容器中。使用<ailas>別名時,SpringIOC容器首先將別名元素所定義的別名註冊到容器中。對於既不是<import>元素,又不是<alias>元素的元素,即Spring配置文件中普通的<bean>元素的解析由BeanDefinitionParserDelegate類的parseBeanDefinitionElement方法來實現。 

下一篇:Spring IOC容器的初始化(二)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章