前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >在使用 SpringMVC 时,Spring 容器是如何与 Servlet 容器进行交互的?

在使用 SpringMVC 时,Spring 容器是如何与 Servlet 容器进行交互的?

作者头像
张乘辉
发布2020-03-25 18:31:24
2.5K0
发布2020-03-25 18:31:24
举报
文章被收录于专栏:后端进阶后端进阶

最近都在看小马哥的 Spring 视频教程,通过这个视频去系统梳理一下 Spring 的相关知识点,就在一个晚上,躺床上看着视频快睡着的时候,突然想到当我们在使用 SpringMVC 时,Spring 容器是如何与 Servlet 容器进行交互的?虽然在我的博客上还有几年前写的一些 SpringMVC 相关源码分析,其中关于 Spring 容器如何与 Servlet 容器进行交互并没有交代清楚,于是趁着这个机会,再撸一次 SpringMVC 源码。

Spring 容器的加载

可否还记得,当年还没有 Springboot 的时候,在 Tomcat 的 web.xml 中进行面向 xml 编程的青葱岁月?其中有那么几段配置总是令我记忆犹新:

首先是 Spring 容器配置:

代码语言:javascript
复制
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:spring-config.xml</param-value>
</context-param>

其次是 Servlet 容器监听器配置:

代码语言:javascript
复制
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在 Tomcat 启动时,根据这两段配置,究竟做了什么动作,使得 Tomcat 与 Spring 完美地结合在一起了呢?

首先我们来看下 ContextLoaderListener 监听器的源码:

我们发现它继承了 ContextLoader,并且实现了 ServletContextListener 接口,下面说下这两个东西的作用:

  1. ContextLoader:正如其名,ContextLoader 可以在启动时载入 IOC 容器;
  2. ServletContextListener:ServletContextListener 接口有两个抽象方法,contextInitialized 和 contextDestroyed,该监听器会结合 Web 容器的生命周期被调,ContextLoaderListener 正是实现了该接口。

因此,ContextLoaderListener 最主要的作用就是在 Tomcat 启动时,根据配置加载 Spring 容器。

以上就是 ContextLoaderListener 实现 contextInitialized 方法的逻辑,也是加载并初始化 Spring 容器的开始。

org.springframework.web.context.ContextLoader#initWebApplicationContext

以上代码逻辑主要做了以下几个操作:

  1. 调用 createWebApplicationContext 方法创建一个容器,会创建一个 contextClass 类型的容器,如果没有配置,则默认创建 WebApplicationContext 类型的容器;
  2. 将容器强转为 ConfigurableWebApplicationContext 类型;
  3. 调用 configureAndRefreshWebApplicationContext 方法初始化 Spring 容器;
  4. 最后将 Spring 容器,以一个元素的形式保存到 Servlet 容器中,这也就意味着,得到 Servlet 容器,同时也可以得到 Spring 容器。

还发现 Spring 容器保存到 Servlet 容器中的 key 为 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,我们顺藤摸瓜找到获取 Spring 容器的方法:

org.springframework.web.context.support.WebApplicationContextUtils#getWebApplicationContext

关于这个方法在哪里调用后面有说到。

org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext

以上是 Spring 容器初始化逻辑,其中,CONFIG_LOCATION_PARAM 即是我们在 xml 中配置的 contextConfigLocation 参数:

同时还会将 Servlet 容器保存到 Spring 容器中,最后调用 refresh 方法进行初始化。

在将 Spring 容器初始化最后以一个元素的形式保存到 Servlet 容器之后,那么 SpringMVC 在初始化时,是如何拿到 Spring 容器的呢?

我们继续看 SpringMVC 初始化是怎么操作的。

SpringMVC 容器的加载

SpringMVC 本质上来讲,就是一个大号的 Servlet,其各种机制都是围绕着一个名叫 DispatcherServlet 的 Servlet 展开的,因此它必然实现了 Servlet 接口,那么在 Tomcat 启动时,它必然会通过 Servlet#init 方法进行初始化动作,我在其调用链路上发现以下方法:

org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

DispatcherServlet 的父类同样有一个方法,该方法是加载 SpringMVC 容器,即源码中的 webApplicationContext:

我们发现,rootContext 就是 ContextLoaderListener 加载的 Spring 容器,在这里,它会以父容器的身份保存到 SpringMVC 容器中。

当然,如果用 Springboot 环境,那么默认只会存在一个上下文环境,原因如下:

1、在 Springboot 应用程序启动时,在 SpringBootServletInitializer#onStartup 方法中,会创建一个 rootAppContext 容器,如下:

同时将上文所说的 ContextLoaderListener 监听器添加到 Servlet 容器中,同样达到了 xml 配置的效果,而调用 createRootApplicationContext 方法创建 rootAppContext 容器时,会将 contextClass 设置为 AnnotationConfigServletWebServerApplicationContext.class。

2、DispatcherServlet 此时作为一个 Bean,实现了 ApplicationContextAware 接口,会自动将上下文环境保存到 webApplicationContext 字段中;

DispatcherServlet 初始化时,经过 debug 可以看到,rootContext 和 webApplicationContext 是同一个实例对象:

原因是通过 ContextLoaderListener 加载的上下文环境,通过 ApplicationContextAware 接口自动 set 进来保存到 DispatcherServlet 的 webApplicationContext 变量中了。

在 FrameworkServlet#initWebApplicationContext 方法最后,最终会将 webApplicationContext 注入以一个元素的形式保存到 Servlet 容器中:

DispatcherServlet 初始化

最终,SpringMVC 初始化会调用该方法:

org.springframework.web.servlet.DispatcherServlet#onRefresh

DispatcherServlet 初始化时,从 Spring 容器中获取相关 Bean,初始化各种不同的组件,比如初始化 HandlerMapping:

总结

本质上来讲,Servlet 容器与 Spring 容器并不互通,但因为有 Servlet 容器的监听器 ServletContextListener,在它们之间构筑了一道桥梁。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端进阶 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring 容器的加载
  • SpringMVC 容器的加载
  • DispatcherServlet 初始化
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档