传统的SpringMVC项目中,需要在web.xml中配置ContextlistenerContextLoaderListener是负责引导启动和关闭Spring的Root上下文的监听器。主要将处理委托给ContextLoaderContextCleanupListener

类的继承关系。



ContextLoaderListener实现了ServletContextListener接口。该接口主要定义了两个行为:监听上下文创建(contextInitialized)和监听上下文销毁(contextDestroyed)。

ContextLoaderListener只是将方法的处理委托给ContextLoader

  1. package org.springframework.web.context;
  2. import javax.servlet.ServletContextEvent;
  3. import javax.servlet.ServletContextListener;
  4. public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
  5. public ContextLoaderListener() {
  6. }
  7. public ContextLoaderListener(WebApplicationContext context) {
  8. super(context);
  9. }
  10. //初始化Root WebApplication
  11. @Override
  12. public void contextInitialized(ServletContextEvent event) {
  13. //委托给ContextLoader
  14. initWebApplicationContext(event.getServletContext());
  15. }
  16. //销毁Root WebApplication
  17. @Override
  18. public void contextDestroyed(ServletContextEvent event) {
  19. //委托给ContextLoader
  20. closeWebApplicationContext(event.getServletContext());
  21. //委托给ContextCleanupListener
  22. ContextCleanupListener.cleanupAttributes(event.getServletContext());
  23. }
  24. }

ContextLoaderinitWebApplicationContext方法负责创建root web application context。

  1. //初始化root web application context
  2. public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
  3. //校验是否已经存在root application,
  4. if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
  5. throw new IllegalStateException(
  6. "Cannot initialize context because there is already a root application context present - " +
  7. "check whether you have multiple ContextLoader* definitions in your web.xml!");
  8. }
  9. Log logger = LogFactory.getLog(ContextLoader.class);
  10. servletContext.log("Initializing Spring root WebApplicationContext");
  11. if (logger.isInfoEnabled()) {
  12. logger.info("Root WebApplicationContext: initialization started");
  13. }
  14. long startTime = System.currentTimeMillis();
  15. try {
  16. // Store context in local instance variable, to guarantee that
  17. // it is available on ServletContext shutdown.
  18. //创建WebApplicationContext,并保存到变量中,等关闭时使用
  19. if (this.context == null) {
  20. this.context = createWebApplicationContext(servletContext);
  21. }
  22. //如果是ConfigurableWebApplicationContext,则配置上下文并刷新
  23. if (this.context instanceof ConfigurableWebApplicationContext) {
  24. ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
  25. if (!cwac.isActive()) {
  26. // The context has not yet been refreshed -> provide services such as
  27. // setting the parent context, setting the application context id, etc
  28. if (cwac.getParent() == null) {
  29. // The context instance was injected without an explicit parent ->
  30. // determine parent for root web application context, if any.
  31. ApplicationContext parent = loadParentContext(servletContext);
  32. cwac.setParent(parent);
  33. }
  34. configureAndRefreshWebApplicationContext(cwac, servletContext);
  35. }
  36. }
  37. //在ServletContext中设置属性,表明已经配置了root web application context
  38. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
  39. ClassLoader ccl = Thread.currentThread().getContextClassLoader();
  40. if (ccl == ContextLoader.class.getClassLoader()) {
  41. currentContext = this.context;
  42. }
  43. else if (ccl != null) {
  44. currentContextPerThread.put(ccl, this.context);
  45. }
  46. if (logger.isDebugEnabled()) {
  47. logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
  48. WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
  49. }
  50. if (logger.isInfoEnabled()) {
  51. long elapsedTime = System.currentTimeMillis() - startTime;
  52. logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
  53. }
  54. return this.context;
  55. }
  56. catch (RuntimeException ex) {
  57. logger.error("Context initialization failed", ex);
  58. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
  59. throw ex;
  60. }
  61. catch (Error err) {
  62. logger.error("Context initialization failed", err);
  63. servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
  64. throw err;
  65. }
  66. }

该方法主要分为四步:

  1. 校验是否已经存在root WebApplicationContext
  2. 创建root WebApplicationContext
  3. 配置上下文并刷新
  4. 绑定root WebApplicationContext到servlet context上

这四步具体分析如下:

1.校验

校验的过程比较简单,主要是判断Servlet Context是否已经绑定过WebApplicationContext

2.创建对象

createWebApplicationContext()方法负责创建WebApplicationContext对象,主要过程是确定WebApplicationContext类,并实例化:

  1. protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
  2. //查找WebApplicationContext的类
  3. Class<?> contextClass = determineContextClass(sc);
  4. if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
  5. throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
  6. "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
  7. }
  8. //实例化对象
  9. return
  10. (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  11. }

决定WebApplicationContext类的主要策略是先判断ServletContext是否配置了contextClass属性,如果是,则用该属性指定的类作为WebApplicationContext类,否则则是否默认策略。默认策略是根绝classpath下的ContextLoader.properties中配置的类作为WebApplicatioNCOntext类:

  1. protected Class<?> determineContextClass(ServletContext servletContext) {
  2. //优先根据servletContext配置的contextClass属性
  3. String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
  4. if (contextClassName != null) {
  5. try {
  6. return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
  7. }
  8. catch (ClassNotFoundException ex) {
  9. throw new ApplicationContextException(
  10. "Failed to load custom context class [" + contextClassName + "]", ex);
  11. }
  12. }
  13. //在通过classpath下的`ContextLoader.properties`文件制定的类作为WebApplicationContext类
  14. //默认是XmlWebApplicationContext
  15. else {
  16. contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
  17. try {
  18. return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
  19. }
  20. catch (ClassNotFoundException ex) {
  21. throw new ApplicationContextException(
  22. "Failed to load default context class [" + contextClassName + "]", ex);
  23. }
  24. }
  25. }
配置与刷新

创建WebApplicationContext对象后,ContextLoader会判断是否是ConfigurableWebApplicationContext

ConfigurableWebApplicationContext拓展了WebApplicationContext的能力。其不仅扩展了自身方法,还继承了ConfigurableApplicationContext接口。

配置与刷新的步骤也正是利用了这些接口提供的能力。

  1. //配置并刷新上下文
  2. protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
  3. //设置ID
  4. if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
  5. // The application context id is still set to its original default value
  6. // -> assign a more useful id based on available information
  7. String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
  8. if (idParam != null) {
  9. wac.setId(idParam);
  10. }
  11. else {
  12. // Generate default id...
  13. wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
  14. ObjectUtils.getDisplayString(sc.getContextPath()));
  15. }
  16. }
  17. //WebApplicationContext绑定Servlet Context
  18. wac.setServletContext(sc);
  19. String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
  20. if (configLocationParam != null) {
  21. wac.setConfigLocation(configLocationParam);
  22. }
  23. // The wac environment's #initPropertySources will be called in any case when the context
  24. // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  25. // use in any post-processing or initialization that occurs below prior to #refresh
  26. ConfigurableEnvironment env = wac.getEnvironment();
  27. if (env instanceof ConfigurableWebEnvironment) {
  28. ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
  29. }
  30. //自定义上下文初始过程,留给用户的拓展机制
  31. customizeContext(sc, wac);
  32. //刷新上下文,加载bean
  33. wac.refresh();
  34. }

创建流程图如下:

SpringMVC Root WebApplicationContext启动流程的更多相关文章

  1. SpringMVC源码解析-DispatcherServlet启动流程和初始化

    在使用springmvc框架,会在web.xml文件配置一个DispatcherServlet,这正是web容器开始初始化,同时会在建立自己的上下文来持有SpringMVC的bean对象. 先从Dis ...

  2. 项目tomcat启动停在Initializing Spring root WebApplicationContext

    来源于:http://ourteam.iteye.com/blog/1270699 某日,再次启动项目,spring一直停在这一句: Initializing Spring root WebAppli ...

  3. 关于Tomcat启动时,长时间停在Initializing Spring root webApplicationContext处的原因

    1.大家肯定经常会遇到这样的问题,以前启动tomcat都不会出问题.现在一起动就会卡到Initializing Spring root webApplicationContext处,tomcat会报连 ...

  4. tomcat启动时非常慢,启动时 一直卡在Root WebApplicationContext: initialization completed(转)

    转载地址:https://www.cnblogs.com/youxin/p/8793554.html 使用方式: JAVA_OPTS="-Djava.awt.headless=true -D ...

  5. tomcat启动时非常慢,启动时 一直卡在Root WebApplicationContext: initialization completed

    每次重启自己的服务tomcat都需要卡住很长时间,每次都是日志停在 Root WebApplicationContext: initialization completed in 744 ms这个地方 ...

  6. 启动web项目卡在Initializing Spring root WebApplicationContext不动

    这几天在和同学一起做一个电教器材管理系统的Web项目,用SVN互通,在此记录下经常遇到的bug. Bug: 启动项目一直卡在Initializing Spring root WebApplicatio ...

  7. Tomcat 启动卡在 Root WebApplicationContext: initialization completed in

    tomcat 启动一直卡在 Root WebApplicationContext: initialization completed in 重启了很多次,更换jdk版本,tomcat版本都不行. to ...

  8. Spring基础系列-容器启动流程(1)

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9870339.html 概述 ​ 我说的容器启动流程涉及两种情况,SSM开发模式和Spri ...

  9. Spring MVC启动流程分析

    本文是Spring MVC系列博客的第一篇,后续会汇总成贴子. Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目 ...

随机推荐

  1. Elasticsearch 核心术语概念

    Elasticsearch 相当于一个关系型数据库 索引 index 类型 type 文档 document 字段 fields 跟关系型数据库对比 Elasticsearch 相当于一个数据库 索引 ...

  2. Github基础使用教程 ———功能介绍

    Github基础使用手把手教程    --功能介绍 本人Github小白,刚摸索的差不多,记录一下经验,小白写出来的东西各位萌新一定看的懂啦~ 本篇内容主要针对想快速学会使用Github这个强大工具的 ...

  3. 曹工说Redis源码(4)-- 通过redis server源码来理解 listen 函数中的 backlog 参数

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  4. Centos7.x & RedHat7.x系统忘记 root 密码解决办法

    重启系统进入引导页面 先将机器重启 根据提示按下e进入内核编辑页面 找到linux16参数行,并在行尾加上rd.break,之后按下Ctrl+X重启 如上图所示,重启之后将进入救援模式. 这是依次输入 ...

  5. Unity Shader and Effects Cookbook问题记录

    1.p61的specular计算,涉及到的一个参数“_SpecColor”是在Unity的官方cginc文件(UnityLightingCommon.cginc)中,是直接赋颜色给这个参数,反应到你模 ...

  6. 机器学习4- 多元线性回归+Python实现

    目录 1 多元线性回归 2 多元线性回归的Python实现 2.1 手动实现 2.1.1 导入必要模块 2.1.2 加载数据 2.1.3 计算系数 2.1.4 预测 2.2 使用 sklearn 1 ...

  7. 一口气说出 4种 LBS “附近的人” 实现方式,面试官笑了

    引言 昨天一位公众号粉丝和我讨论了一道面试题,个人觉得比较有意义,这里整理了一下分享给大家,愿小伙伴们面试路上少踩坑.面试题目比较简单:"让你实现一个附近的人功能,你有什么方案?" ...

  8. java的多线程是如何实现的?和操作系统有什么关系?

    本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 本文操作系统是centos7 1.查看 pthread_create 函数显示及其示例 ...

  9. labview 机器视觉

    学习labview机器视觉,一定要安装VAS,VDM.先安装labview,再安装VAS和VDM. 安装完成后,前面板出现vision 后面板出现视觉与运动函数

  10. d3.js v4曲线图的拖拽功能实现Zoom

    zoom缩放案例 源码:https://github.com/HK-Kevin/d...:demo:https://hk-kevin.github.io/d3...: 原理:通过zoom事件来重新绘制 ...