zoukankan      html  css  js  c++  java
  • springMVC初始化流程(二)

    由上一篇 springMVC核心代码(一)中最后一个问题,spring mvc 提供的控制器类型handlerMappings  是如何被初始化的,来引出SpringMVC初始化。那么先看HandlerMappings初始化源码可得:

     其初始化是在DispatcherServlet类中的这个方法进行的,而这个初始化方法initHandlerMappings()又是什么时候执行的呢?在该类中发现,是在下面方法中执行初始化的,同时发现好多组件都是在这个方法中执行,比如另一个重要的对象控制器适配器

    那么归根究底,其sprinngMVC的初始化是在onRefresh()这个方法中执行的。

    因此下面首先通过时序图来探索springMVC的初始化流程:

     servlet初始化会调用 init 方法,换句话说就是springMVC进行初始化的时候首先会去执行HttpServletBean的init方法, 下面看看HttpServletBean的源码:

    public final void init() throws ServletException {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
            }
           //从init参数设置bean属性
            PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
            if (!pvs.isEmpty()) {
                try {
                    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                    ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
                    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
                    this.initBeanWrapper(bw);
                    bw.setPropertyValues(pvs, true);
                } catch (BeansException var4) {
                    if (this.logger.isErrorEnabled()) {
                        this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
                    }
     
                    throw var4;
                }
            }
           //让子类做任何他们喜欢的初始化
            this.initServletBean();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
            }
     
        }

    上面这段代码主要是在获取你在web.xml中配置在<init-param>中的属性(例如: namespace, contextConfigLocation)。 其中有一点值得注意,那就是 initServletBean() 这个方法是由其子类 FrameworkServlet 实现,因此, 接下来 FramworkServlet 会执行 initServletBean 这个方法,下面就继续看看 initServletBean 方法源码:

    protected final void initServletBean() throws ServletException {
            this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
            if (this.logger.isInfoEnabled()) {
                this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
            }
     
            long startTime = System.currentTimeMillis();
     
            try {
                this.webApplicationContext = this.initWebApplicationContext();
                this.initFrameworkServlet();
            } catch (RuntimeException | ServletException var5) {
                this.logger.error("Context initialization failed", var5);
                throw var5;
            }
     
            if (this.logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
            }
     
        }

    可以看到 initServletBean 方法中就调用了一个 initFrameworkServlet 方法和 initWebApplicationContext 方法,其中initFrameworkServlet方法是由子类实现,这个不多说,直接看 initWebApplicationContext 方法源码:

    protected WebApplicationContext initWebApplicationContext() {
     
            //此处的 rootContext 在你配置了ContextLoaderListener的时候注入的
            //通过分析ContextLoaderListenr的源码,可以看到
            //ContextLoaderListener通过ContextLoader根据ApplicationContext.xml的配置会创建一个xmlWebApplicationContext
            //如果没有配置ContextLoaderListener,本处将为null,但不影响springMVC,为何?通过接下来的分析,就能看到原因
            WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
            WebApplicationContext wac = null;
           
           //当webApplicationContext已经存在,那么就直接使用,使用之前会先设置rootContext,为其跟。
           //配置完成之后refresh一次,refresh会涉及到IOC的内容,本处不做探讨。
     
            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);
                    }
                }
            }
            //如果不存在webApplicationContext,那么先去ServletContext中查找
            if (wac == null) {
                wac = this.findWebApplicationContext();
            }
            //如果上述没有查到,那么就创建webApplicationContext
            if (wac == null) {
                wac = this.createWebApplicationContext(rootContext);
            }
     
            if (!this.refreshEventReceived) {
                 //此方法由DispatcherServlet调用
                this.onRefresh(wac);
            }
            //将webApplicationContext保存在ServletContext
            if (this.publishContext) {
                //将上下文发布为servlet上下文属性。
                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;
        }

    上面代码调用了一次 createWebApplicationContext 方法, 下面就看看这个方法的源码:

    protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
            
           //此处的contextClass 可在web.xml 中的《init-param》中指定
           //如果没有配置,那么默认的是XmlWebApplicationContext.class
            Class<?> contextClass = this.getContextClass();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]");
            }
     
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
            } else {
                 //此处利用反射创建
                ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
                wac.setEnvironment(this.getEnvironment());
                wac.setParent(parent);
                String configLocation = this.getContextConfigLocation();
                if (configLocation != null) {
                    wac.setConfigLocation(configLocation);
                }
                //refresh一次,这里不多说
                this.configureAndRefreshWebApplicationContext(wac);
                return wac;
            }
        }

    以上就是创建webApplicationContext的代码,注释已给出。

    还记得FrameworkServlet初始化的时候会调用onRefresh()方法吗,这个方法是留给其子类DispatcherServlet调用的, 最后就该看看DispatcherServlet里面的 onRefresh 方法了:

    protected void onRefresh(ApplicationContext context) {
            this.initStrategies(context);
        }
         //初始化此servlet使用的策略对象。
         //可以在子类中重写以初始化其他策略对象
        protected void initStrategies(ApplicationContext context) {
            this.initMultipartResolver(context);
            this.initLocaleResolver(context);
            this.initThemeResolver(context);
            this.initHandlerMappings(context);
            this.initHandlerAdapters(context);
            this.initHandlerExceptionResolvers(context);
            this.initRequestToViewNameTranslator(context);
            this.initViewResolvers(context);
            this.initFlashMapManager(context);
        }

    可以看到onRefresh方法就一句话,调用initStrategies方法, 上面给出了 initStrategies 源码, 很直观,就是在初始化springMVC 的一系列组件, 但是此处你要明白,SpringMVC的组件其实已经在webApplicationContext创建时就已经实例化了, 此处所谓的初始化只是在选择合适的组件(每一个组件都对应了几个不同的实现)。

    到此,就可以很清楚知道,HandlerMappings和HandlerAdapter等是在什么时候初始化的。


    原文链接:https://blog.csdn.net/gududedabai/article/details/83375156



  • 相关阅读:
    linux上的工具或软件
    百度分享插件怎么取消鼠标放在图片上出现的分享条
    常用的Lambda表达式
    IDEA安装使用Lombok插件
    Spring cloud Eureka错误锦集(二)
    Spring cloud Eureka错误锦集(一)
    Navicat破解
    手动添加jar包到本地仓库
    Markdown常用语法
    Java中的位运算符
  • 原文地址:https://www.cnblogs.com/loong-hon/p/12910227.html
Copyright © 2011-2022 走看看