新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring 源码剖析:IoC 容器在 Web 环境(Tomcat)中的启动原理

发布时间:2026/9/13 19:57:51来源:尧图网络
Spring 源码剖析:IoC 容器在 Web 环境(Tomcat)中的启动原理
Spring 源码剖析IoC 容器在 Web 环境Tomcat中的启动原理【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter导读在 Web 应用中SpringMVC 是建立在 Spring IoC 容器之上的理解 SpringMVC 的第一步是搞清楚 IoC 容器是如何被载入 Web 容器以 Tomcat 为例并完成初始化的。本文将结合本仓库 IoC容器在Web环境中的启动.md 的主线逐层剖析web.xml部署描述、ContextLoaderListener监听器、ContextLoader启动器、WebApplicationContext上下文体系以及XmlWebApplicationContext的实现并穿插 SpringMVC的设计与实现.md 中的源码证据。读完本文你将掌握 Spring 在 Web 容器中建立「根上下文 子上下文」层次化体系的全过程理解DispatcherServlet与根上下文之间的双亲-子级关系以及各类配置参数contextConfigLocation、contextClass等的底层作用。1 Web 环境中的 SpringMVCweb.xml 是接口部分Spring 的 IoC 是一个独立模块它并不直接在 Web 容器中发挥作用。要在 Web 环境中使用 IoC 容器Spring 必须设计一个与 Web 容器启动过程集成在一起的启动流程一方面处理 Web 容器的启动另一方面通过特定的 Web 容器拦截器Listener将 IoC 容器载入 Web 环境并完成初始化。只有这个流程完成后IoC 容器才能正常工作SpringMVC 才能在此基础上建立 MVC 运行机制响应来自 Web 容器的 HTTP 请求。以 Tomcat 为例web.xml是应用的部署描述文件也是 SpringMVC 与 Tomcat 之间的接口部分。典型的配置如下servlet servlet-namesample/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class load-on-startup6/load-on-startup /servlet servlet-mapping servlet-namesample/servlet-name url-pattern/*/url-pattern /servlet-mapping context-param param-namecontextConfigLocation/param-name param-value/WEB-INF/applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener这段部署描述中包含了三部分关键信息Servlet 定义配置了 SpringMVC 的DispatcherServlet它是 MVC 中十分重要的类起着分发请求的作用。load-on-startup6/load-on-startup指定了 Servlet 的启动顺序与优先级。URL 映射url-pattern/*/url-pattern限定了该 Servlet 需要处理的 HTTP 请求范围。context-param 参数contextConfigLocation用于指定 IoC 容器读取 Bean 的 XML 文件的路径此处为WEB-INF/applicationContext.xml其中包含 Spring 应用的 Bean 配置。监听器ContextLoaderListener被定义为 Spring MVC 的启动类该监听器与 Web 服务器的生命周期相关联负责完成 IoC 容器在 Web 环境中的启动工作。DispatcherServlet和ContextLoaderListener提供了在 Web 容器中对 Spring 的接口它们与 Web 容器的耦合是通过ServletContext实现的ServletContext是容器与应用沟通的桥梁从一定程度上讲ServletContext就是 Servlet 规范的体现。ServletContext为 Spring 的 IoC 容器提供了宿主环境Spring MVC 在其中建立起 IoC 容器体系先通过ContextLoaderListener的初始化建立容器体系再把DispatcherServlet作为 Spring MVC 处理 Web 请求的转发器建立起来从而完成响应 HTTP 请求的准备。2 IoC 容器启动的基本过程层次化上下文体系的建立IoC 容器的启动过程就是建立上下文的过程该上下文与ServletContext相伴而生是 IoC 容器在 Web 应用环境中的具体表现之一。由ContextLoaderListener启动的上下文为根上下文Root Context在根上下文之上还有一个与 Web MVC 相关的子上下文用来保存控制器DispatcherServlet所需的 MVC 对象作为根上下文的子上下文构成一个层次化的上下文体系。在 Web 容器中启动 Spring 应用程序时首先建立根上下文然后建立这个上下文体系该过程由ContextLoader完成整体时序如下图所示。ContextLoaderListener是 Spring 提供的类为在 Web 容器中建立 IoC 容器服务。它实现了 Servlet API 中定义的ServletContextListener接口该接口提供了与 Servlet 生命周期结合的回调上下文初始化contextInitialized()方法和上下文销毁contextDestroyed()方法。在 Web 容器中建立WebApplicationContext的过程是在contextInitialized()方法中完成的。另外ContextLoaderListener还继承了ContextLoader具体的 IoC 容器载入过程由ContextLoader完成。在ContextLoader中完成了两个 IoC 容器建立的基本过程在 Web 容器中建立起双亲 IoC 容器根上下文生成相应的WebApplicationContext并将其初始化。3 Web 容器中的上下文设计3.1 WebApplicationContext 接口为 Web 环境扩展的上下文为了方便在 Web 环境中使用 IoC 容器Spring 为 Web 应用提供了上下文的扩展接口WebApplicationContext其继承关系如下图所示。在这个类继承关系中可以从熟悉的XmlWebApplicationContext入手来了解它的接口实现。接口设计上最终通过ApplicationContext接口与BeanFactory接口对接而对于具体的功能实现很多都封装在其基类AbstractRefreshableWebApplicationContext中完成该抽象基类提供了refresh()刷新上下文的模板实现AnnotationConfigWebApplicationContext、GenericWebApplicationContext等同级实现类也共享这套骨架。WebApplicationContext接口中定义了一个关键常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE用来索引存储在ServletContext中的根上下文同时定义了getServletContext()方法通过它可得到当前 Web 容器的 Servlet 上下文环境相当于提供了一个 Web 容器级别的全局环境public interface WebApplicationContext extends ApplicationContext { /** * 该常量用于在 ServletContext 中存取根上下文 */ String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE WebApplicationContext.class.getName() .ROOT; /** * 对于 WebApplicationContext 来说需要得到 Web容器 的 ServletContext */ ServletContext getServletContext(); }3.2 XmlWebApplicationContextWeb 环境中的默认 IoC 容器在启动过程中Spring 会使用默认的WebApplicationContext实现作为 IoC 容器即XmlWebApplicationContext。它继承了ApplicationContext在ApplicationContext的基础上增加了对 Web 环境和 XML 配置定义的处理。在XmlWebApplicationContext的初始化过程中Web 容器中的 IoC 容器被建立起来从而在 Web 容器中建立起整个 Spring 应用。与普通 IoC 容器初始化一样这个过程同样有loadBeanDefinitions()方法对 BeanDefinition 的载入。在 Web 环境中对定位 BeanDefinition 的 Resource 有特别的要求这个要求体现在对getDefaultConfigLocations()方法的处理中——Spring 将默认的 BeanDefinition 配置路径作为一个常量定义好了即/WEB-INF/applicationContext.xmlpublic class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { /** 若不指定其它文件Spring 默认从 /WEB-INF/applicationContext.xml 目录文件 初始化 IoC容器 */ public static final String DEFAULT_CONFIG_LOCATION /WEB-INF/applicationContext.xml; /** 默认的配置文件在 /WEB-INF/ 目录下 */ public static final String DEFAULT_CONFIG_LOCATION_PREFIX /WEB-INF/; /** 默认的配置文件后缀名为 .xml */ public static final String DEFAULT_CONFIG_LOCATION_SUFFIX .xml; /** * 此加载过程在容器 refresh() 时启动 */ Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 使用 XmlBeanDefinitionReader 对指定的 BeanFactory 进行解析 XmlBeanDefinitionReader beanDefinitionReader new XmlBeanDefinitionReader(beanFactory); // 初始化 beanDefinitionReader 的属性其中设置 ResourceLoader 是因为 XmlBeanDefinitionReader // 是 DefaultResource 的子类所有这里同样会使用 DefaultResourceLoader 来定位 BeanDefinition beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 该方法是一个空实现 initBeanDefinitionReader(beanDefinitionReader); // 使用初始化完成的 beanDefinitionReader 来加载 BeanDefinitions loadBeanDefinitions(beanDefinitionReader); } protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) { } /** * 获取所有的配置文件然后一个一个载入 BeanDefinition */ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException { String[] configLocations getConfigLocations(); if (configLocations ! null) { for (String configLocation : configLocations) { reader.loadBeanDefinitions(configLocation); } } } /** * 获取默认路径 /WEB-INF/***.xml 下的配置文件 * 或者获取 /WEB-INF/applicationContext.xml 配置文件 */ Override protected String[] getDefaultConfigLocations() { if (getNamespace() ! null) { return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX getNamespace() DEFAULT_CONFIG_LOCATION_SUFFIX}; } else { return new String[] {DEFAULT_CONFIG_LOCATION}; } } }从上面的代码可以看到在XmlWebApplicationContext中基本的上下文功能都已通过类的继承获得这里需要处理的核心问题是如何在 Web 容器环境中获取 BeanDefinition 信息。获取到 BeanDefinition 信息之后后续过程与普通 IoC 容器一致——通过XmlBeanDefinitionReader载入 BeanDefinition 信息最终完成整个上下文的初始化。关于 BeanDefinition 的定位、解析与注册可进一步参阅仓库中的 BeanDefinition的资源定位过程.md、将bean解析封装成BeanDefinition.md 与 将BeanDefinition注册进IoC容器.md。4 ContextLoaderListener 与 ContextLoader根上下文的启动器4.1 监听器与 Web 容器生命周期的绑定对于 Spring 承载的 Web 应用而言可以指定在 Web 应用程序启动时载入 IoC 容器即WebApplicationContext。这个功能由ContextLoaderListener完成它是在 Web 容器中配置的监听器监听 Web 容器的启动然后载入 IoC 容器。ContextLoaderListener通过使用ContextLoader来完成实际的WebApplicationContextIoC 容器的初始化工作ContextLoader就像 Spring 应用程序在 Web 容器中的启动器。ContextLoaderListener是启动根 IoC 容器并把它载入到 Web 容器的主要功能模块也是整个 Spring Web 应用加载 IoC 的第一个地方。从加载过程可以看到首先从 Servlet 事件中得到ServletContext然后读取配置在web.xml中的各个相关属性值接着ContextLoader实例化WebApplicationContext并完成其载入和初始化过程。这个被初始化的第一个上下文作为根上下文存在载入后被绑定到 Web 应用程序的ServletContext上。任何需要访问根上下文的应用程序代码都可以通过WebApplicationContextUtils类的静态方法得到。ContextLoaderListener实现的是ServletContextListener接口这个接口里的函数会结合 Web 容器的生命周期被调用服务器启动时ServletContext被创建触发contextInitialized()回调服务器关闭时ServletContext被销毁触发contextDestroyed()回调。在初始化回调中ContextLoaderListener创建ContextLoader并利用它完成 IoC 容器的初始化public class ContextLoaderListener extends ContextLoader implements ServletContextListener { private ContextLoader contextLoader; /** * 启动 web应用 的根上下文 */ public void contextInitialized(ServletContextEvent event) { // 由于本类直接继承了 ContextLoader所以能直接使用 ContextLoader 来初始化 IoC容器 this.contextLoader createContextLoader(); if (this.contextLoader null) { this.contextLoader this; } // 具体的初始化工作交给 ContextLoader 完成 this.contextLoader.initWebApplicationContext(event.getServletContext()); } }4.2 ContextLoader根上下文的创建与发布ContextLoader中定义了多个与web.xml初始化参数对应的常量用于从ServletContext读取配置常量对应 web.xml 参数作用CONTEXT_CLASS_PARAMcontextClass指定使用何种WebApplicationContext实现类CONTEXT_ID_PARAMcontextId为根上下文指定 IDCONTEXT_INITIALIZER_CLASSES_PARAMcontextInitializerClasses指定上下文初始化器类CONFIG_LOCATION_PARAMcontextConfigLocation指定 Bean 配置文件位置LOCATOR_FACTORY_SELECTOR_PARAMlocatorFactorySelector定位父上下文的工厂选择器LOCATOR_FACTORY_KEY_PARAMparentContextKey定位父上下文的 key此外ContextLoader在静态代码块中从ContextLoader.properties加载默认策略defaultStrategies该文件中的默认策略目前严格内部使用不建议应用开发者定制。initWebApplicationContext(ServletContext)是根上下文初始化的核心方法其流程如下唯一性检查如果ServletContext中已存在ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE对应的根上下文则抛出IllegalStateException提示检查web.xml中是否存在多个ContextLoader*定义。创建根上下文调用createWebApplicationContext(servletContext)实例化上下文对象。设置双亲上下文若上下文未激活且没有父上下文调用loadParentContext(servletContext)载入根上下文的双亲上下文默认为null。配置并刷新调用configureAndRefreshWebApplicationContext(cwac, servletContext)其中refresh()方法就是之前 IoC 容器初始化分析中AbstractApplicationContext的刷新入口。发布根上下文通过servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context)把根上下文存入ServletContext此后所有应用都根据ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE常量获取根上下文。记录当前上下文根据线程上下文类加载器将根上下文记录到currentContext或currentContextPerThread按类加载器隔离中供后续检索。public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 如果 ServletContext 中已经包含了根上下文则抛出异常 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) ! null) { throw new IllegalStateException( Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!); } Log logger LogFactory.getLog(ContextLoader.class); servletContext.log(Initializing Spring root WebApplicationContext); if (logger.isInfoEnabled()) { logger.info(Root WebApplicationContext: initialization started); } long startTime System.currentTimeMillis(); try { if (this.context null) { // 这里创建在 ServletContext 中存储的根上下文 this.context createWebApplicationContext(servletContext); } if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac (ConfigurableWebApplicationContext) this.context; if (!cwac.isActive()) { if (cwac.getParent() null) { // 载入根上下文的 双亲上下文 ApplicationContext parent loadParentContext(servletContext); cwac.setParent(parent); } // 配置并初始化 IoC容器看到下面方法中的 Refresh单词 应该能想到 // AbstractApplicationContext 中的 refresh()方法猜到它是前面介绍的 IoC容器 的初始化入口 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 将上面创建的 WebApplicationContext实例 存到 ServletContext 中注意同时被存入的常量 // ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE以后的应用都会根据这个属性获取根上下文 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl Thread.currentThread().getContextClassLoader(); if (ccl ContextLoader.class.getClassLoader()) { currentContext this.context; } else if (ccl ! null) { currentContextPerThread.put(ccl, this.context); } if (logger.isDebugEnabled()) { logger.debug(Published root WebApplicationContext as ServletContext attribute with name [ WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE ]); } if (logger.isInfoEnabled()) { long elapsedTime System.currentTimeMillis() - startTime; logger.info(Root WebApplicationContext: initialization completed in elapsedTime ms); } return this.context; } catch (RuntimeException ex) { logger.error(Context initialization failed, ex); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } catch (Error err) { logger.error(Context initialization failed, err); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); throw err; } }值得注意的细节当初始化抛出RuntimeException或Error时异常/错误对象本身也会被存入ServletContext的同一属性中。这正是 SpringMVC的设计与实现.md 中WebApplicationContextUtils.getWebApplicationContext(ServletContext, String)会先检查属性值是否为RuntimeException/Error/Exception并重新抛出或包装的原因——初始化失败信息会被延迟到任何后续访问方读取时暴露出来。4.3 determineContextClass如何决定使用哪种 IoC 容器createWebApplicationContext(ServletContext)通过determineContextClass(sc)判断在 Web 容器中使用什么类作为 IoC 容器并要求该类必须是ConfigurableWebApplicationContext的子类型最后通过BeanUtils.instantiateClass(contextClass)直接实例化。determineContextClass的判定逻辑体现了配置优先级首先从ServletContext中读取contextClass初始化参数即web.xml中context-param的配置若配置了则通过ClassUtils.forName加载该自定义上下文类若未配置contextClass则从ContextLoader.properties的默认策略中读取WebApplicationContext.class.getName()对应的默认实现类——即XmlWebApplicationContext。protected Class? determineContextClass(ServletContext servletContext) { // 获取 servletContext 中对 CONTEXT_CLASS_PARAMcontextClass参数 的配置 String contextClassName servletContext.getInitParameter(CONTEXT_CLASS_PARAM); if (contextClassName ! null) { try { // 获取配置的 contextClassName 对应的 clazz对象 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( Failed to load custom context class [ contextClassName ], ex); } } else { // 如果没有配置 CONTEXT_CLASS_PARAM则使用默认的 ContextClass contextClassName defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( Failed to load default context class [ contextClassName ], ex); } } }4.4 configureAndRefreshWebApplicationContext配置与刷新的入口configureAndRefreshWebApplicationContext负责为根上下文装配运行环境生成上下文 ID若上下文 ID 仍是默认值则优先使用contextId初始化参数否则根据 Servlet 版本生成Servlet 2.4 及以下使用web.xml中的servlet-context-nameServlet 2.5 使用contextPath均以ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX为前缀。设置 ServletContext 与配置文件位置wac.setServletContext(sc)将当前 Web 容器的ServletContext绑定到上下文若存在contextConfigLocation初始化参数则调用wac.setConfigLocation(initParameter)设置 Bean 配置文件路径。自定义上下文调用customizeContext(sc, wac)可被子类覆盖用于注册ApplicationContextInitializer等。刷新容器调用wac.refresh()这是 IoC 容器初始化的真正入口与普通 IoC 容器启动完全一致。protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // - assign a more useful id based on available information String idParam sc.getInitParameter(CONTEXT_ID_PARAM); if (idParam ! null) { wac.setId(idParam); } else { // Generate default id... if (sc.getMajorVersion() 2 sc.getMinorVersion() 5) { // Servlet 2.4: resort to name specified in web.xml, if any. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX ObjectUtils.getDisplayString(sc.getServletContextName())); } else { wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX ObjectUtils.getDisplayString(sc.getContextPath())); } } } // 设置 ServletContext 及配置文件的位置参数 wac.setServletContext(sc); String initParameter sc.getInitParameter(CONFIG_LOCATION_PARAM); if (initParameter ! null) { wac.setConfigLocation(initParameter); } customizeContext(sc, wac); // IoC容器 初始化的入口想不起来的把前面 IoC容器 初始化的博文再读10遍 wac.refresh(); }5 根上下文如何被 DispatcherServlet 复用双亲-子级上下文体系根上下文初始化完成后会被存储到ServletContext中从而建立了一个全局的、关于整个应用的上下文。在启动 SpringMVC 时DispatcherServlet进行自己持有的上下文初始化时会从ServletContext中得到根上下文并将其设置为 DispatcherServlet 自带上下文的双亲上下文。这一过程发生在FrameworkServletDispatcherServlet的父类中。FrameworkServlet.initWebApplicationContext()的核心逻辑通过WebApplicationContextUtils.getWebApplicationContext(getServletContext())从ServletContext中取出根上下文作为当前 MVC 上下文的双亲上下文依次尝试三种上下文来源构造时注入的上下文实例this.webApplicationContext→ 已注册在 Servlet 上下文中的上下文findWebApplicationContext()→ 新建本地上下文createWebApplicationContext(rootContext)若未收到刷新事件则手动触发onRefresh(wac)若publishContext为true将当前 Servlet 的上下文以getServletContextAttributeName()与 Servlet 名称相关的属性名存入ServletContext保证该上下文在 Web 环境上下文体系中的唯一性。protected WebApplicationContext initWebApplicationContext() { // 获取根上下文作为当前 MVC上下文 的双亲上下文这个根上下文保存在 ServletContext 中 WebApplicationContext rootContext WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac null; if (this.webApplicationContext ! null) { // 可以在本对象被构造时注入一个 webApplicationContext实例 wac this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // 上下文尚未刷新 - 提供诸如设置父上下文、设置应用程序上下文id等服务 if (cwac.getParent() null) { // 上下文实例在没有显式父实例的情况下被注入 - // 将根上下文如果有的话可以为空设置为父上下文 cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac null) { // 在本对象被构造时没有注入上下文实例 - // 查看是否已在 servlet上下文 中注册了上下文实例。 // 如果存在一个则假定父上下文如果有的话已经被设置 // 并且用户已经执行了任何初始化例如设置上下文ID wac findWebApplicationContext(); } if (wac null) { // 没有为此 servlet 定义上下文实例 - 创建本地实例 wac createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // 上下文不是支持刷新的 ConfigurableApplicationContext或者 // 在构造时注入的上下文已经完成刷新 - 在此处手动触发 onRefresh()方法 onRefresh(wac); } if (this.publishContext) { // 把当前建立的上下文保存到 ServletContext 中使用的属性名是和 当前servlet名 相关的 String attrName getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug(Published WebApplicationContext of servlet getServletName() as ServletContext attribute with name [ attrName ]); } } return wac; }创建 DispatcherServlet 持有的上下文时FrameworkServlet.createWebApplicationContext(parent)与ContextLoader.createWebApplicationContext的过程非常相似默认上下文类同样是XmlWebApplicationContext通过getContextClass()获取可通过contextClass参数定制实例化后设置父上下文、环境、配置文件位置等最终同样通过refresh()完成初始化。WebApplicationContextUtils是封装了静态方法的抽象工具类任何需要访问根上下文的代码都可以通过它获取public abstract class WebApplicationContextUtils { /** * 使用了 WebApplicationContext 的 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性获取 * ServletContext 中的根上下文这个属性代表的根上下文在 ContextLoaderListener 初始化的 * 过程中被建立 */ public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } /** * 查找此 web应用程序 的自定义 WebApplicationContext */ public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { Assert.notNull(sc, ServletContext must not be null); Object attr sc.getAttribute(attrName); if (attr null) { return null; } if (attr instanceof RuntimeException) { throw (RuntimeException) attr; } if (attr instanceof Error) { throw (Error) attr; } if (attr instanceof Exception) { throw new IllegalStateException((Exception) attr); } if (!(attr instanceof WebApplicationContext)) { throw new IllegalStateException(Context attribute is not of type WebApplicationContext: attr); } return (WebApplicationContext) attr; } }理解了这个双亲-子级上下文体系就掌握了 Spring Web 应用中 Bean 的共享规则一个根上下文可以作为多个 Servlet 上下文的双亲上下文向子上下文getBean()时IoC 容器会首先向其双亲上下文查询因此在根上下文中定义的 Bean 可以被各个 Servlet 持有的上下文获取和共享。这也是业务 Bean 放在根上下文applicationContext.xml、而 MVC 组件控制器、视图解析器等放在 DispatcherServlet 上下文中的经典分工的底层依据。完整的请求分发细节可进一步阅读 SpringMVC的设计与实现.md。6 总结Web 环境中 IoC 容器的启动全景IoC 容器在 Web 容器中的启动过程与应用中启动 IoC 容器的方式相类似所不同的是需要考虑 Web 容器的环境特点比如各种参数的设置、IoC 容器与 Web 容器ServletContext的结合等。整体流程可归纳为Tomcat 启动→ 解析web.xml实例化ContextLoaderListener实现了ServletContextListener触发contextInitialized()→ 创建ContextLoader调用initWebApplicationContext(servletContext)ContextLoader创建根上下文→determineContextClass决定上下文实现类默认XmlWebApplicationContext实例化后设置ServletContext、contextConfigLocation等参数refresh()完成 IoC 容器初始化→ 通过XmlBeanDefinitionReader载入 BeanDefinition走完 Bean 解析、注册、依赖注入全流程发布根上下文→ 以ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE为属性名存入ServletContext供全局访问初始化 DispatcherServlet→FrameworkServlet.initWebApplicationContext()从ServletContext取出根上下文作为双亲上下文创建并刷新自己的 MVC 上下文将其发布到ServletContext属性名与 Servlet 名称相关。初始化完成后的上下文会被存储到ServletContext中这样就建立了一个全局的、关于整个应用的上下文。同时在启动 SpringMVC 时这个根上下文会被DispatcherServlet在初始化自己持有的上下文时设置为双亲上下文——正是这一设计让 SpringMVC 得以建立在 IoC 容器之上构成完整的 Web 应用上下文体系。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

无人机飞控二次开发:树莓派外挂与MAVLink分层实践指南 2026/9/13 20:39:55

无人机飞控二次开发:树莓派外挂与MAVLink分层实践指南

1. 别急着编译px4——先搞清“二次开发”到底在开发什么 很多人一听说“飞控二次开发”,第一反应就是clone px4或ardupilot仓库,打开vscode,对着一堆C文件发呆。我见过太多人花两周配好环境、编译成功、烧录进飞控板,结果连LED灯怎…

阅读更多 →
嵌入式工程师进大厂必备:7大底层认知链深度解析 2026/9/13 20:39:55

嵌入式工程师进大厂必备:7大底层认知链深度解析

1. 这不是“背题清单”,而是嵌入式工程师进大厂前必须打通的底层认知链你刷到这份标题时,大概率正坐在凌晨一点的台灯下,对着一份PDF反复划线,或是把“volatile作用”“中断上下文为什么不能sleep”抄了第三遍——但第二天模拟面试…

阅读更多 →
Docker Hub镜像发布全流程详解:从构建推送到自动化运维 2026/9/13 20:39:55

Docker Hub镜像发布全流程详解:从构建推送到自动化运维

先交代一句:这个标题看着简单,但真正把镜像发布到 Docker Hub 并让团队、用户能顺利拉取,中间藏着的坑一点都不少。我见过太多人卡在docker push权限报错上,也见过不少镜像 push 上去了但 tag 乱成一团,还有人被 Docke…

阅读更多 →
SPARK 0代码架构:AI驱动的自动化开发实践 2026/9/13 20:39:55

SPARK 0代码架构:AI驱动的自动化开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
文件监控与同步实战:从inotify到rsync的高可靠实现 2026/9/13 20:39:55

文件监控与同步实战:从inotify到rsync的高可靠实现

说实话,我最早接触文件监控与同步,不是因为技术兴趣,而是被一份日志备份的需求逼出来的。线上服务散在好几台机器上,日志文件分散在各自的磁盘里,每次排查问题都得一台一台登录上去翻,折腾到半夜是常有的事…

阅读更多 →
Apache Airflow Amazon Provider:使用 ImapAttachmentToS3Operator 将邮件附件从 IMAP 服务器迁移到 Amazon S3 2026/9/13 20:36:55

Apache Airflow Amazon Provider:使用 ImapAttachmentToS3Operator 将邮件附件从 IMAP 服务器迁移到 Amazon S3

Apache Airflow Amazon Provider:使用 ImapAttachmentToS3Operator 将邮件附件从 IMAP 服务器迁移到 Amazon S3 【免费下载链接】airflow Apache Airflow - A platform to programmatically author, schedule, and monitor workflows 项目地址: https://gitcode.c…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞