美文网首页
Spring Mvc研究

Spring Mvc研究

作者: IceBins | 来源:发表于2017-10-18 13:05 被阅读0次
    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研究

          本文链接:https://www.haomeiwen.com/subject/wztxsxtx.html