Spring IOC容器在Web环境中是如何启动的(源码级剖析)?

文章目录

在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):

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 环境中的启动工作。

核心机制:

  1. 关键组件 :通过DispatchServlet(请求转发器)和ContextLoaderListener(容器初始化监听器)实现与Web容器的对接。
  2. 耦合机制 :基于ServletContext实现与Web容器的解耦,ServletContext作为servlet规范的体现,既是容器与应用的桥梁,又为Spring IoC容器提供宿主环境。
  3. 容器体系构建 :ContextLoaderListener负责初始化建立IoC容器体系,随后初始化DispatchServlet作为请求处理器。
  4. 完整流程:这两个组件协同工作,使基于IoC容器的Spring MVC能够完成HTTP请求的接收、处理和响应。

下面是 Servlet 3.0+ 规范后我们可以直接使用Java配置类的方式实现web.xml相同功能;

Java配置类(Servlet 3.0+):

java 复制代码
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{RootConfig.class}; 
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};  
    }

    @Override
    protected 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




核心要点:

  1. 创建Web根上下文容器实例:
    • 创建在 ServletContext 中存储的Web根上下文容器 (Spring IOC容器);会根据ServletContext中获取contextClass参数的配置来创建指定的IoC容器,默认使用XmlWebApplicationContext作为在Web环境中使用的IoC容器。
    • 直接通过反射实例化需要的IoC容器
  2. 载入根上下文的双亲上下文:
    • 判断当前Web根上下文的Parent context为null时为当前 Web 应用的根上下文设置一个父上下文,从而支持上下文的继承和共享,适用于需要分层或共享资源的复杂应用场景。
  3. 核心初始化方法(配置并刷新容器):
    • 为当前Web应用容器配置容器Web环境
    • 加载配置文件(从web.xml的contextConfigLocation参数);
    • 将 ServletContext 和 ServletConfig 中的属性添加到容器环境中,以便在后续的应用上下文初始化过程中使用;
    • 扩展点,可对 ConfigurableWebApplicationContext 进行自定义配置,在上下文刷新(refresh())之前被调用;
    • IoC容器的初始化(调用AbstractApplicationContext.refresh())

四、扩展:Web容器中的上下文设计

Spring框架为Web应用提供了专用的上下文(IOC容器)的扩展接口类 WebApplicationContext 来满足Web环境中启动过程的需要,其主要继承关系如下:

WebApplicationContext

核心功能点:

WebApplicationContext 作为 Spring 框架中一个核心接口 ,主要用于为 Web 应用程序提供配置。它扩展了 ApplicationContext 接口,增加了与 Web 环境相关的功能。以下是其主要作用:

  1. 提供Web容器 ServletContext 的访问 :
    允许访问标准的 Servlet API 的 ServletContext 对象,用于与底层 Web 容器交互。
  2. 支持 Web 特定的作用域 :
    定义了 Web 特定的作用域标识符,例如:
    • SCOPE_REQUEST:请求作用域。
    • SCOPE_SESSION:会话作用域。
    • SCOPE_APPLICATION:全局 Web 应用作用域。
  3. 与 Web 环境相关的 Bean 定义 :
    提供了与 ServletContext 和初始化参数相关的 Bean 名称,例如:
    • SERVLET_CONTEXT_BEAN_NAME:ServletContext 的 Bean 名称。
    • CONTEXT_PARAMETERS_BEAN_NAME:ServletContext 初始化参数的 Bean 名称。
    • CONTEXT_ATTRIBUTES_BEAN_NAME:ServletContext 属性的 Bean 名称。
  4. 支持层次化上下文 :
    Web 应用程序上下文是分层的,整个应用程序有一个根上下文,每个 Servlet(如 Spring MVC 的 DispatcherServlet)有自己的子上下文。
  5. 与 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容器初始化流程的无缝衔接。

五、总结与最佳实践

通过源码分析,我们揭示以下关键机制:

  1. Web上下文容器启动 :由ContextLoaderListener监听Web容器(Tomcat)的启动来创建初始化web应用的根上下文(父IOC容器)
  2. Web上下文容器刷新 :refresh()方法是容器初始化的核心入口,继承自父类的功能实现,包含12个关键步骤(BeanFactory创建、后处理器注册、单例初始化等)
  3. Web上下文父子容器交互 :子容器通过setParent()方法引用父容器;Bean查找时通过getParentBeanFactory()实现委托

深入理解这些底层机制,将帮助开发者更好地诊断启动问题、优化应用性能,并为复杂场景(如动态注册Bean)提供解决方案。


ContextLoaderListener Diagram


End!

相关推荐
weixin_BYSJ1987几秒前
【计算机毕设】基于Spring_Boot_Vue的马拉松报名与选手成绩追踪系统05445
java·vue.js·spring boot·学习·spring·django·课程设计
Shaoshing27 分钟前
消息类型
java
2301_322414280431 分钟前
springboot学生课堂出勤统计系统15835-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·mysql·随机森林·微信小程序·uni-app·微信开发者工具·推荐算法
程序员夏洛1 小时前
VMware Workstation Pro 卸载失败:安装向导提前结束、错误 1603 的排查与解决
java
User_芊芊君子2 小时前
【JavaEE初阶 - 网络原理】网络分几层?从自定义协议聊到 DNS
java·网络·java-ee
聚美智数9 小时前
智能结构化识别-图片结构解析-图像结构化解析-照片结构化解析
android·java·数据库
十年Java程序媛9 小时前
Java枚举Enum完整教程|替代静态常量,开发与面试要点
java·开发语言·spring boot·后端·面试
我是人✓9 小时前
SpringBoot静态资源映射揭秘
java·spring boot·后端
土豆.exe10 小时前
nf_tables 的 verdict 被读了两次:一个十年未闭合的位域错配
java·数据库·网络安全·缓存
小坏讲微服务11 小时前
刚发布一个月的JDK 27,为什么我建议你立刻升级?
java·开发语言·spring boot·spring·架构·求职招聘·springboot4