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对象

    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

你可能感兴趣的:(Spring Mvc研究)