• 分析配置DispatcherServlet类时load-on-startup标签作用


    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:application.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    SpringMVC中DispatcherServlet是该框架的核心,所有的请求处理及返回都要经过该Servlet,于是我们必须在web.xml里面配置该Servlet,大多时候我们在配置该Servlet的时候都会顺手配置一下:<load-on-startup>数值</load-on-startup>元素。起初就是以为加载DispatcherServlet的,我们来详细研究一下关于load-on-startup的作用及其过程。

    作用:load-on-startup元素标记容器表示是否在启动的时候就加载这个servlet(实例化并调用其init()方法),而<load-on-startup>x</load-on-startup>中x的取值1,2,3,4,5代表的是优先级,而非启动延迟时间。

    过程:

    1、因为该servlet是实例化并调用init()方法的,我们先进入该类寻找init()方法

     从该类中可看出其定义的几个resolver都是由final修饰,表示该属性一旦被初始化便不可改变,这里不可改变的意思对基本类型来说是其值不可变,而对对象属性来说其引用不可再变。

    所以我们在配置这些的时候不能随意定值,必须和其一致,如下示例:

    <!--application.xml中的multipartResolver的id是个定值,如果写错了就会报错-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:defaultEncoding="UTF-8" p:maxInMemorySize="1024000"></bean>

    经查找并未在DispatcherServlet中找到init()方法,我们往上一级找其父类(FrameworkServlet)未果,再往上一级(HttpServletBean)通过查找在HttpServletBean中找到了该方法

        /**
         * Map config parameters onto bean properties of this servlet, and
         * invoke subclass initialization.
         * @throws ServletException if bean properties are invalid (or required
         * properties are missing), or if subclass initialization fails.
         */
        @Override
        public final void init() throws ServletException {
            if (logger.isDebugEnabled()) {
                logger.debug("Initializing servlet '" + getServletName() + "'");
            }
    
            // Set bean properties from init parameters.
            PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
            if (!pvs.isEmpty()) {
                try {
                    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                    initBeanWrapper(bw);
                    bw.setPropertyValues(pvs, true);
                }
                catch (BeansException ex) {
                    if (logger.isErrorEnabled()) {
                        logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
                    }
                    throw ex;
                }
            }
    
            // Let subclasses do whatever initialization they like.
            initServletBean();
    
            if (logger.isDebugEnabled()) {
                logger.debug("Servlet '" + getServletName() + "' configured successfully");
            }
        }

    由其介绍可知其作用大概是,map配置参数到这个servlet的bean属性,并调用子类初始化,其子类初始化的处理就在于initServletBean();的作用,我们去寻找此方法的来源

    (HttpServletBean)中找到了,但是个空方法,我们往回继续找在(FrameworkServlet)中找到了

        /**
         * Overridden method of {@link HttpServletBean}, invoked after any bean properties
         * have been set. Creates this servlet's WebApplicationContext.
         */
        @Override
        protected final void initServletBean() throws ServletException {
            getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
            if (this.logger.isInfoEnabled()) {
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
            }
            long startTime = System.currentTimeMillis();
    
            try {
                this.webApplicationContext = initWebApplicationContext();
                initFrameworkServlet();
            }
            catch (ServletException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
            catch (RuntimeException ex) {
                this.logger.error("Context initialization failed", ex);
                throw ex;
            }
    
            if (this.logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                        elapsedTime + " ms");
            }
        }

    我们打开initWebApplicationContext()方法:

        /**
         * Initialize and publish the WebApplicationContext for this servlet.
         * <p>Delegates to {@link #createWebApplicationContext} for actual creation
         * of the context. Can be overridden in subclasses.
         * @return the WebApplicationContext instance
         * @see #FrameworkServlet(WebApplicationContext)
         * @see #setContextClass
         * @see #setContextConfigLocation
         */
        protected WebApplicationContext initWebApplicationContext() {
            WebApplicationContext rootContext =
                    WebApplicationContextUtils.getWebApplicationContext(getServletContext());
            WebApplicationContext wac = null;
    
            if (this.webApplicationContext != null) {
                // A context instance was injected at construction time -> use it
                wac = this.webApplicationContext;
                if (wac instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                    if (!cwac.isActive()) {
                        // The context has not yet been refreshed -> provide services such as
                        // setting the parent context, setting the application context id, etc
                        if (cwac.getParent() == null) {
                            // The context instance was injected without an explicit parent -> set
                            // the root application context (if any; may be null) as the parent
                            cwac.setParent(rootContext);
                        }
                        configureAndRefreshWebApplicationContext(cwac);
                    }
                }
            }
            if (wac == null) {
                // No context instance was injected at construction time -> see if one
                // has been registered in the servlet context. If one exists, it is assumed
                // that the parent context (if any) has already been set and that the
                // user has performed any initialization such as setting the context id
                wac = findWebApplicationContext();
            }
            if (wac == null) {
                // No context instance is defined for this servlet -> create a local one
                wac = createWebApplicationContext(rootContext);
            }
    
            if (!this.refreshEventReceived) {
                // Either the context is not a ConfigurableApplicationContext with refresh
                // support or the context injected at construction time had already been
                // refreshed -> trigger initial onRefresh manually here.
                onRefresh(wac);
            }
    
            if (this.publishContext) {
                // Publish the context as a servlet context attribute.
                String attrName = getServletContextAttributeName();
                getServletContext().setAttribute(attrName, wac);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                            "' as ServletContext attribute with name [" + attrName + "]");
                }
            }
    
            return wac;
        }

    整个过程就是为了给wac赋值,并返回该值,最后执行了onRefresh()方法:

    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    然后执行initStrategies方法:

    /**
    * Initialize the strategy objects that this servlet uses.
    * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
    
        //用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File.
        initMultipartResolver(context); 
    
        //SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候。
        initLocaleResolver(context);  
    
        //用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、
        //如图片、css样式等。SpringMVC的主题也支持国际化,
         initThemeResolver(context); 
    
        //用来查找Handler的。
        initHandlerMappings(context); 
    
        //从名字上看,它就是一个适配器。Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。
        //如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情
        initHandlerAdapters(context);
    
        //其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?
        //这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。
        initHandlerExceptionResolvers(context); 
    
        //有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,
        //如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。
        initRequestToViewNameTranslator(context);
    
         //ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。
        //View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
        initViewResolvers(context);
    
         //用来管理FlashMap的,FlashMap主要用在redirect重定向中传递参数。
        initFlashMapManager(context); 
    }

    至此就完成了整个初始化过程了。

  • 相关阅读:
    Java Web-session介绍
    Java异常-一般异常和运行时异常的区别
    Spring控制Hibernate的缓存机制ehcache
    Hibernate-缓存
    Hibernate-入门教程
    Java算法-hash算法
    Java算法-各种题目总结
    Java算法-堆排序
    Java算法-快速排序
    Java算法-归并排序
  • 原文地址:https://www.cnblogs.com/yimengxianzhi/p/12323971.html
Copyright © 2020-2023  润新知