Spring|Spring Mvc研究

  1. 首先在web.xml中配置了listener, 并配置了参数contextConfigurationLocation,指定context的配置文件路径, 默认配置的listener为org.springframework.web.context.ContextLoaderListener,该类继承自ContextLoader类
  2. ServletContext启动之后会调用listener的contextInitialized函数,查看下函数
this.initWebApplicationContext(event.getServletContext()); 最终会调用ContextLoader的initWebApplicationContext函数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!"); } else { 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) { this.context = this.createWebApplicationContext(servletContext); }if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context; if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = this.loadParentContext(servletContext); cwac.setParent(parent); }this.configureAndRefreshWebApplicationContext(cwac, servletContext); } }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 var8) { logger.error("Context initialization failed", var8); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8); throw var8; } catch (Error var9) { logger.error("Context initialization failed", var9); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9); throw var9; } }

在initWebApplicationContext函数中主要是做了三件事:创建webApplicationContext, 然后将context设置到servletContext(名称为WebApplicationContext.ROOT)中, 最后映射类加载器和创建的实例到currentContextPerThread中。
  1. 接下来是DispatchServlet的处理,
    3.1 按照《源码深度解析》一书中所说, servlet有三个阶段:
    • 初始化阶段
      • 加载Servlet类;
      • 创建ServletConfig对象, 包含了servlet的初始化配置信息
      • 创建servlet对象, 并调用其init函数初始化
    • 运行阶段
      • servlet容器创建servletRequest对象和servletResponse对象
      • 调用service函数处理并返回结果
    • 销毁阶段
      • servlet容器首先调用servlet对象的destroy函数, 然后销毁servlet对象, 销毁servletConfig对象
    【Spring|Spring Mvc研究】3.2. DispatchServlet -> init(将servlet对象转换成BeanWrapper对象,便于spring容易管理以及参数注入)
    • 加载参数, 即servlet配置中的init-param,包装类为ServletConfigPropertyValues
    • 把当前的servlet包装成BeanWrapper
    • 注册resourceLoader
    • initBeanWrapper
    • initServletBean
      该函数在父类FrameServlet中实现, 完成两件事:initWebApplicationContext() 和 initFrameworkServlet(留给子类实现)
      • initWebApplicationContext函数如下:
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac; if (!cwac.isActive()) { if (cwac.getParent() == null) { cwac.setParent(rootContext); }this.configureAndRefreshWebApplicationContext(cwac); } } }if (wac == null) { wac = this.findWebApplicationContext(); }if (wac == null) { wac = this.createWebApplicationContext(rootContext); }if (!this.refreshEventReceived) { this.onRefresh(wac); }if (this.publishContext) { String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } }return wac; }

  • 首先从servletContext中尝试获得WebApplicationContext,这个是由contextlistener设置的,若不为空执行2, 否则执行3
  • configureAndRefreshWebApplicationContext
    • 尝试设置servletContext, servletConfig, namespace等参数
    • 根据profile选择不同的参数源
    • applyInitializer
    • 最终会执行refresh函数, 最终都会执行AbstractApplicatonContext中提供的refresh函数, 完成bean容器的初始化
  • 若context为null, 可能的原因是设置的key不对, 则尝试重新从servletContext中获取
  • 若context仍然为null, 则尝试重新创建WebApplicationContext, 设置一些常用参数如contextConfigurationLocation等, 并执行configureAndRefreshWebApplicationContext.
  1. 执行onRefresh函数, 在FrameworkServlet中会触发onRefresh函数, 最终会调用DispatchServlet的onRefresh函数.
  • initMultipartResolver
  • initLocaleResolver
  • initThemeResolver
  • initHandlerMappings
    • HandlerMapping是根据request获得HandlerExecutionChain, 即执行链
    • 默认情况下(detectAllHandlerMappings)会加载所有的实现HandlerMapping接口的类, 并按照优先级排序。
  • initHandlerAdapters
  • initHandlerExceptionResolvers
  • initRequestToViewNameTranslator
  • initViewResolvers
  • initFlashMapManager

    推荐阅读