文章目录
- 一、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!