Spring源碼探究:IoC容器在Web容器中的創(chuàng)建及初始化

關(guān)于IoC容器和控制反轉(zhuǎn)(也被稱為依賴注入)模式以及Spring IoC的應(yīng)用場景我在這里就不進(jìn)行贅述了,下面直接深入到Spring的源碼當(dāng)中來探究一下IoC容器究竟是如何工作的。

在文章開頭,我們要明確兩個概念:

  • IoC容器的創(chuàng)建就是我們創(chuàng)建一個容器,使其擁有IoC容器的基本結(jié)構(gòu);(下文中提到的的createWebApplicationContext完成的是IoC容器的創(chuàng)建工作)
  • 在完成IoC容器創(chuàng)建的前提下進(jìn)行bean的注冊以及依賴注入之后才算完成了IoC容器的初始化(下文中提到的ConfigureAndRefreshWebApplicationContext進(jìn)行的是IoC容器的初始化工作但是關(guān)于容器初始化的細(xì)節(jié)我將放到后面的博客進(jìn)行介紹)。

在Spring IoC容器系列的設(shè)計(jì)中,我們可以看到兩個兩個主要的容器系列:一個是實(shí)現(xiàn)BeanFactory接口的簡單容器系列,這個系列容器只實(shí)現(xiàn)了容器的最基本功能;另一個是ApplicationContext應(yīng)用上下文,它作為容器的高級形態(tài)而存在。應(yīng)用上下文在簡單容器的基礎(chǔ)上,增加了許多面向框架的特性,同時對應(yīng)用環(huán)境作了許多適配。有了這兩種基本的容器系列,基本上可以滿足用戶對IoC容器使用的大部分需求了。下面我們主要來看看IoC容器在Web容器中是如何進(jìn)行初始化的。

既然我們要探究IoC容器初始化的過程,那就要問一個問題:
IoC容器什么時候會進(jìn)行初始化的行為呢?

我們先來看一個非?;A(chǔ)的場景:

web.xml配置文件中的一部分

上面是web.xml配置文件中的一部分

  • contextConfigLocation對應(yīng)的value是Spring配置文件的絕對路徑;
  • 下面這個監(jiān)聽器主要用來對Servlet容器(在這里指的是Tomcat)的行為進(jìn)行監(jiān)聽

我們先來看看監(jiān)聽器類ContextLoaderListener中有什么東西:

ContextLoaderListener源碼

我們可以發(fā)現(xiàn)ContextLoaderListener繼承自ContextLoader,并且還實(shí)現(xiàn)了ServletContextListener。并且它的構(gòu)造函數(shù)中傳入了一個WebApplicationContext,它是繼承自ApplicationContext接口的高級IoC容器。

ServletContextListener源碼

ServletContextListener是Servlet中比較重要的一個接口:它的作用是用來監(jiān)聽Servlet容器的啟動和銷毀事件。所以在ContextLoaderListener中:

  • contextInitialized方法的入?yún)⒒蚴潜O(jiān)聽的Event是ServletContextEvent事件,也就是Tomcat啟動加載完web.xml會產(chǎn)生的事件,ServletContextEvent持有了從web.xml加載的初始化配置的ServletContext上下文。
  • ContextDestroyed方法的入?yún)⒒蚴潜O(jiān)聽的Event是ServletContextEvent事件,在Tomcat關(guān)閉的時候執(zhí)行該方法。

所以我們現(xiàn)在可以先捋一下流程:當(dāng)Servlet容器啟動事件發(fā)生時,將被ContextLoaderListen監(jiān)聽器監(jiān)聽到。此時ContextLoaderListener會調(diào)用實(shí)現(xiàn)ServletContextListener接口后實(shí)現(xiàn)的contextInitialized方法,并把在web.xml加載初始化后獲取的ServletContext傳入initWebApplicationContext函數(shù)中進(jìn)行IoC容器的初始化。

因?yàn)閕nitWebApplicationContext函數(shù)是從ContextLoader繼承過來的,所以我們現(xiàn)在進(jìn)入ContextLoader源碼中看一看。

ContextLoader類中的靜態(tài)代碼塊

映入眼簾的是個靜態(tài)代碼塊:

  • 創(chuàng)建一個ClassPathResource對象,同時把值為"ContextLoader.properties"的一個常量作為參數(shù)傳入。易知ContextLoader.properties文件與ContextLoader類是在同一個目錄下的;ContextLoader.properties文件內(nèi)容如下:
org.springframework.web.context.WebApplicationContext=
org.springframework.web.context.support.XmlWebApplicationContext

因此我們可以得知Spring默認(rèn)初始化的是XmlWebApplicationContext容器

  • 得到一個Properties對象,后面講根據(jù)類名來創(chuàng)建對應(yīng)的ApplicationContext容器

下面來看看initiWebApplicationContext方法


initWebApplicationContext方法源碼

我們現(xiàn)在可以接著剛才的流程繼續(xù)講下去:當(dāng)調(diào)用ContextLoaderListener中的initWebApplicationContext的函數(shù)并且將獲取到的servletContext作為參數(shù)傳入之后,initWebApplicationContext首先會嘗試從servletContext中獲取根容器,如果容器不為空,則容器初始化失敗---因?yàn)閣eb.xml中可能定義了多個IoC容器的加載器。假如此時容器還未初始化,則調(diào)用createWebApplicationContext方法來創(chuàng)建一個容器。創(chuàng)建完容器之后,將會調(diào)用一個非常重要的configureAndRefreshWebApplicationContext方法。在執(zhí)行這個方法的時候,會將從ApplicationContext.xml配置文件中獲取到的內(nèi)容配置到已經(jīng)創(chuàng)建好了的XmlWebApplicationContext容器中去,并調(diào)用refresh方法來完成容器的初始化。然后,再將已經(jīng)完成初始化的XmlWebApplicationContext容器注冊到servletContext中去。

其實(shí)在Web容器中,ServletContext為Spring的IoC容器提供了宿主環(huán)境,對應(yīng)的建立起一個IoC容器的體系。其中,首先需要建立的是根上下文,這個上下文持有的對象可以有業(yè)務(wù)對象、數(shù)據(jù)存取對象、資源、事務(wù)管理器等各種中間層對象。在這個上下文的基礎(chǔ)上,與Web MVC相關(guān)還會有一個上下文來保持控制器之類的MVC對象,這樣就構(gòu)成了一個層次化的上下文結(jié)構(gòu)。因?yàn)樵趇nitWebApplicationContext方法中我們可以看到其實(shí)創(chuàng)建ApplicationContext容器的工作是交由createWebApplicationContext方法來實(shí)現(xiàn)的,下面我們來看看這個方法:

createWebApplicationContext函數(shù)源碼

createWebApplicationContext函數(shù)功能:

  • 決定要創(chuàng)建的ApplicationContext類型;
  • 實(shí)例化一個ApplicationContext

那么它是如何決定要創(chuàng)建的ApplicationContext類型的呢?
起作用的其實(shí)是方法中第一行的determineContextClass方法

determineContextClass方法源碼

完成了IoC容器的創(chuàng)建之后,在initWebApplicationContext中講調(diào)用configureAndRefreshWebApplicationContext來對該IoC進(jìn)行初始化:

  • 為創(chuàng)建好的IoC容器設(shè)置Web應(yīng)用的上下文,以便二者整合;
  • 為同一個IoC容器設(shè)置配置文件的絕對路徑;
  • 調(diào)用IoC容器的refresh函數(shù)對其進(jìn)行初始化
configureAndRefreshWebApplicationContext函數(shù)

由于IoC容器的初始化涉及到比較多的步驟:包括BeanDefinition的Resource定位等等,所以我將在后面另開博客進(jìn)行介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容