文章目錄
- 一、Web 環境中的 Spring MVC 框架
- 二、Web 應用部署描述配置
- 傳統配置(web.xml):
- Java配置類(Servlet 3.0+):
- 三、核心啟動流程詳解
- 1. 啟動流程圖
- 2. ★容器初始化入口:ContextLoaderListener
- 3. ★容器創建核心:ContextLoader
- 四、擴展:Web容器中的上下文設計
- WebApplicationContext
- 核心功能點:
- 源碼解析
- XmlWebApplicationContext
- 源碼解析
- 五、總結與最佳實踐
在Java Web 應用開發中,Spring的IOC容器同樣扮演著核心角色。Spring IOC(控制反轉)容器的啟動過程需要與 Web 容器【Servlet容器】(如 Tomcat、Jetty)的生命周期集成。本文將深入剖析這一過程的核心機制。
一、Web 環境中的 Spring MVC 框架
在Web環境中,業內主流的是Spring MVC框架, 但它是建立在Spring IoC容器基礎上的。所以想要深入了解Spring MVC框架,首先要了解 Spring IoC 容器是如何在 Web 環境中被載入并生效的。
-
Spring IoC是Spring框架的基石,是一個獨立的模塊,它并不能直接在 Web 容器中發揮作用;要在Web環境中使用IoC容器,則需要 Spring 為它設計一個啟動過程,好引導它在web環境中啟動。
-
具體說來,這個啟動過程是和 Web 容器【Servlet容器】(如 Tomcat、Jetty)的生命周期的啟動過程集成在一起的。在這個過程中,一方面處理 Web 容器的啟動,另一方面通過設計特定的Web容器過濾器,將IoC容器載人到Web環境中并將其初始化。
-
等啟動過程執行完成后Spring IoC容器就能正常工作;而Spring MVC是建立在IoC容器的基礎上的,這樣才能建立起完整MVC框架的運行機制,從而可以接收、響應從Web容器傳遞的HTTP請求。
下面就以 Tomcat 為 Web 容器的進行分析這個啟動過程
二、Web 應用部署描述配置
在 Tomcat 中,web.xml
是應用的部署配置文件。在Spring MVC項目中的 web.xml
中經常能看到與 Spring 相關的部署配置。
傳統配置(web.xml):
<!-- DispatcherServlet配置 -->
<servlet><servlet-name>app</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/app-context.xml</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>app</servlet-name><url-pattern>/*</url-pattern>
</servlet-mapping><context-param><param-name>contextConfigLocation</param-name><param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 根容器初始化 -->
<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
要點解析:
web.xml 這個部署描述文件中,定義了一些對象:
DispatcherServlet
:是 Spring MVC 的DispatcherServlet
,且是一個Servlet
對象。起著分發請求的作用,是MVC框架中很重要的一個類。servlet > init-param
:用來指定 Spring MVC 容器讀取Web Bean定義的XML文件路徑,這里配置文件指定為/WEB-INF/app-context.xml
。servlet-mapping
:為這個DispatcherServlet
定義了對應的URL映射,以指定這個Servlet需要處理的HTTP請求范圍。context-param
參數用來指定 Spring IoC 容器讀取Bean定義的XML文件路徑,在這里,這個配置文件被定義為WEB-INF/applicationContext.xml
。ContextLoaderListener
:核心類,是 Spring MVC 的啟動類,被定義為一個監聽器,它是與Web服務器的生命周期相關聯的,是它負責完成 IoC 容器在 Web 環境中的啟動工作。
核心機制:
- 關鍵組件:通過
DispatchServlet
(請求轉發器)和ContextLoaderListener
(容器初始化監聽器)實現與Web容器的對接。 - 耦合機制:基于
ServletContext
實現與Web容器的解耦,ServletContext
作為servlet
規范的體現,既是容器與應用的橋梁,又為Spring IoC容器提供宿主環境。 - 容器體系構建:
ContextLoaderListener
負責初始化建立IoC容器體系,隨后初始化DispatchServlet
作為請求處理器。 - 完整流程:這兩個組件協同工作,使基于IoC容器的Spring MVC能夠完成HTTP請求的接收、處理和響應。
下面是 Servlet 3.0+ 規范后我們可以直接使用Java配置類的方式實現web.xml
相同功能;
Java配置類(Servlet 3.0+):
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{RootConfig.class}; }@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class}; }@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}
}
下面我們看一下IoC容器在Web環境中的啟動及代碼實現。
三、核心啟動流程詳解
1. 啟動流程圖
2. ★容器初始化入口:ContextLoaderListener
初始化入口類ContextLoaderListener
,源碼位置:org.springframework.web.context.ContextLoaderListener
核心要點:
- 可以看到它實現了
ServletContextListener
接口,這個接口是 Servlet API 中定義的,提供了與 Servlet 生命周期相結合的回調;它是在Web容器中配置的監聽器,作為監聽器當監聽到Web服務器(Tomcat)啟動時,它的contextInitialized()
方法會被調用,從而在這觸發載入 IoC 容器。 - 另外,它還繼承了
ContextLoader
,具體的載入 IoC 容器的過程是由ContextLoader
來完成的。
3. ★容器創建核心:ContextLoader
創建核心類ContextLoader
,源碼位置:org.springframework.web.context.ContextLoader
核心要點:
- 創建Web根上下文容器實例:
- 創建在
ServletContext
中存儲的Web根上下文容器(Spring IOC容器);會根據ServletContext
中獲取contextClass
參數的配置來創建指定的IoC容器,默認使用XmlWebApplicationContext
作為在Web環境中使用的IoC容器。 - 直接通過反射實例化需要的IoC容器
- 創建在
- 載入根上下文的雙親上下文:
- 判斷當前Web根上下文的
Parent context
為null
時為當前 Web 應用的根上下文設置一個父上下文,從而支持上下文的繼承和共享,適用于需要分層或共享資源的復雜應用場景。
- 判斷當前Web根上下文的
- 核心初始化方法(配置并刷新容器):
- 為當前Web應用容器配置容器Web環境
- 加載配置文件(從
web.xml
的contextConfigLocation
參數); - 將
ServletContext
和ServletConfig
中的屬性添加到容器環境中,以便在后續的應用上下文初始化過程中使用; - 擴展點,可對
ConfigurableWebApplicationContext
進行自定義配置,在上下文刷新(refresh()
)之前被調用; - IoC容器的初始化(調用
AbstractApplicationContext.refresh()
)
四、擴展:Web容器中的上下文設計
Spring框架為Web應用提供了專用的上下文(IOC容器)的擴展接口類 WebApplicationContext
來滿足Web環境中啟動過程的需要,其主要繼承關系如下:
WebApplicationContext
核心功能點:
WebApplicationContext
作為 Spring 框架中一個核心接口,主要用于為 Web 應用程序提供配置。它擴展了 ApplicationContext
接口,增加了與 Web 環境相關的功能。以下是其主要作用:
- 提供Web容器
ServletContext
的訪問:
允許訪問標準的 Servlet API 的ServletContext
對象,用于與底層 Web 容器交互。 - 支持 Web 特定的作用域:
定義了 Web 特定的作用域標識符,例如:SCOPE_REQUEST
:請求作用域。SCOPE_SESSION
:會話作用域。SCOPE_APPLICATION
:全局 Web 應用作用域。
- 與 Web 環境相關的 Bean 定義:
提供了與 ServletContext 和初始化參數相關的 Bean 名稱,例如:SERVLET_CONTEXT_BEAN_NAME
:ServletContext
的 Bean 名稱。CONTEXT_PARAMETERS_BEAN_NAME
:ServletContext
初始化參數的 Bean 名稱。CONTEXT_ATTRIBUTES_BEAN_NAME
:ServletContext
屬性的 Bean 名稱。
- 支持層次化上下文:
Web 應用程序上下文是分層的,整個應用程序有一個根上下文,每個Servlet
(如 Spring MVC 的DispatcherServlet
)有自己的子上下文。 - 與
ServletContextAware
的集成:
自動檢測實現了ServletContextAware
接口的 Bean,并調用其setServletContext
方法。
源碼解析
XmlWebApplicationContext
從前面類繼承關系圖中,可以看到前面了解到的默認Web容器實現XmlWebApplicationContext
,它繼承自實現了擴展接口類 WebApplicationContext
的類 。在 ApplicationContext
的基礎上,增加了對 Web 環境 和 XML 配置定義 的處理。
在 XmlWebApplicationContext
的初始化過程中,Web容器(Servlet容器)中的IoC容器隨之被建立起來,具體過程則是從 refresh() 方法入口的, 這里不做過多解析。
下面主要針對XmlWebApplicationContext
源碼了解學習下。
源碼解析
從上面的源代碼中可以看到,在 XmlWebApplicationContext
中并沒有多少功能點,主要處理了如何在Web環境中獲取BeanDefinition
信息,這是因為其繼承的父類已經具備基礎上下文功能(IOC容器的能力)。
總結一下就是:
- ?繼承體系基礎
- 通過繼承
AbstractRefreshableConfigApplicationContext
等父類已具備基礎上下文功能 - 核心挑戰轉為Web環境下BeanDefinition資源的定位與加載
- 通過繼承
- 資源獲取機制
- 解析web.xml中配置的contextConfigLocation參數確定配置文件路徑
- 將ServletContext路徑轉換為Spring可識別的Resource對象
- 定義加載流程
- 使用XmlBeanDefinitionReader讀取XML配置并解析BeanDefinition
- 加載過程與XmlFileSystemBeanFactory類似,但需適配Web資源路徑格式
- 初始化完成
- 通過refresh()方法觸發完整的容器初始化生命周期
- 最終形成包含所有BeanDefinition的可運行上下文環境
該上下文(IOC容器)的設計完美的實現了Web環境與標準IoC容器初始化流程的無縫銜接。
五、總結與最佳實踐
通過源碼分析,我們揭示以下關鍵機制:
- Web上下文容器啟動:由
ContextLoaderListener
監聽Web容器(Tomcat)的啟動來創建初始化web應用的根上下文(父IOC容器) - Web上下文容器刷新:
refresh()
方法是容器初始化的核心入口,繼承自父類的功能實現,包含12個關鍵步驟(BeanFactory創建、后處理器注冊、單例初始化等) - Web上下文父子容器交互:子容器通過
setParent()
方法引用父容器;Bean查找時通過getParentBeanFactory()
實現委托
深入理解這些底層機制,將幫助開發者更好地診斷啟動問題、優化應用性能,并為復雜場景(如動態注冊Bean)提供解決方案。
ContextLoaderListener Diagram
End!