美文网首页spring
Spring MVC源码解读二

Spring MVC源码解读二

作者: 测试你个头 | 来源:发表于2017-02-22 11:31 被阅读0次

    接上一篇:Spring MVC源码解读一

    • DispatchServlet:
      源码位置:

    DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步

    类继承关系:



    时序图:


    • servlet初始化链路:
      通过时序图可以看到,HttpServletBean中调用了initServletBean()方法,通过源代码可以看到是在HttpServletBean的init()中调用了initServletBean()
      HttpServletBean的init()方法源码如下,重写了HttpServlet的init()方法:
        /**
         * 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.
            try {
                PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
                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");
            }
        }
    

    问题: 1、HttpServlet的init()是什么时候调用的? 解答: 在前面解读spring mvc在web.xml中的load-on-startup配置时已经回答过了
    FrameworkServlet中的initServletBean()源码

    /**
         * 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");
            }
        }
    

    可以看到initServletBean()中具体的处理都是通过调用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;
        }
    

    initWebApplicationContext()正如方法名的直观含义所表示的,它的作用就是初始化webApplicationContext,主要步骤有:
    1、通过servletcontext获取ContextLoaderListener中初始化的根WebApplicationContext
    2、调用onRefresh(rootContext)来对webApplicationContext进行初始化(主要是加载mvc相关的BeanDefinition到webApplicationContext中)
    问题: ContextLoaderListener和FrameworkServlet类中都有initWebApplicationContext()方法,二者有什么区别? 解答: a) ContextLoaderListener是监听Servlet的启动/结束,在启动时调用initWebApplicationContext()初始化web的根应用上下文; b) FrameworkServlet调用initWebApplicationContext()时,会先取ContextLoaderListener初始化的web根应用上下文,对其进行mvc相关的初始化:FrameworkServlet的initWebApplicationContext()方法通过调用DispatcherServlet的onRefresh()和initStrategies()方法实现对mvc相关的BeanDefinition加载(具体需要接下来看initStrategies()方法源码)

    由于DispatcherSerlvet的onRefresh直接调用了initStrategies()方法,我们看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) {
            initMultipartResolver(context);
            initLocaleResolver(context);
            initThemeResolver(context);
            initHandlerMappings(context);
            initHandlerAdapters(context);
            initHandlerExceptionResolvers(context);
            initRequestToViewNameTranslator(context);
            initViewResolvers(context);
            initFlashMapManager(context);
        }
    

    我们逐个解读这些初始化方法,他们都是从webApplicationContext中通过getBean()方法获取BeanDefinition的实例:
    1.以initMultipartResolver为例:

        /**
         * Initialize the MultipartResolver used by this class.
         * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
         * no multipart handling is provided.
         */
        private void initMultipartResolver(ApplicationContext context) {
            try {
                this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
                }
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Default is no multipart resolver.
                this.multipartResolver = null;
                if (logger.isDebugEnabled()) {
                    logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
                            "': no multipart request handling provided");
                }
            }
        }
    

    initMultipartResolver从webApplicationContext获取名为“multipartResolver”的Bean定义,如果spring的配置中没有配置的话,webApplicationContext也获取不到该Bean定义,则multipartResolver不生效

    这些初始化方法分别对应spring mvc配置中的这些Bean:
    multipartResolver
    localeResolver
    themeResolver
    handlerMapping:指定handlerMapping的处理类
    handlerAdapter
    handlerExceptionResolver
    viewNameTranslator
    viewResolver:指定视图解析类
    flashMapManager
    例如:viewResolver配置


    我们重点看下initHandlerMappings()的源码,他是从mvc中重要的url mapping的基础:

        /**
         * Initialize the HandlerMappings used by this class.
         * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
         * we default to BeanNameUrlHandlerMapping.
         */
        private void initHandlerMappings(ApplicationContext context) {
            this.handlerMappings = null;
    
            if (this.detectAllHandlerMappings) {
                // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
                Map<String, HandlerMapping> matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
                if (!matchingBeans.isEmpty()) {
                    this.handlerMappings = new ArrayList<>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                    AnnotationAwareOrderComparator.sort(this.handlerMappings);
                }
            }
            else {
                try {
                    HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                    this.handlerMappings = Collections.singletonList(hm);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    // Ignore, we'll add a default HandlerMapping later.
                }
            }
    
            // Ensure we have at least one HandlerMapping, by registering
            // a default HandlerMapping if no other mappings are found.
            if (this.handlerMappings == null) {
                this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
                if (logger.isDebugEnabled()) {
                    logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
                }
            }
        }
    

    这段源码的主要作用是指定用于handlerMapping的具体处理类,并将其赋值给DispatcherServlet的handlerMappings字段,可以单步调试看下HandlerMapping的具体值:


    可以看到handleMappings默认值有3个: 1.RequestMappingHandlerMapping 2.BeanNameUrlHandlerMapping 3.SimpleUrlHandlerMapping

    相关文章

      网友评论

        本文标题:Spring MVC源码解读二

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