zoukankan      html  css  js  c++  java
  • spring mvc DispatcherServlet详解之一---处理请求深入解析

    要深入理解spring mvc的工作流程,就需要先了解spring mvc的架构:

    从上图可以看到 前端控制器DispatcherServlet在其中起着主导作用,理解了DispatcherServlet 就完全可以说弄清楚了spring mvc。

    为了加深对spring mvc的整个工作流程的理解,本文从分析DispatcherServlet的工作过程来一窥spring mvc的整个面貌。

    1. 初始化

    protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); //文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
    initLocaleResolver(context); //本地化解析
    initThemeResolver(context);   //主题解析
    initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器
    initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器
    initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
    initRequestToViewNameTranslator(context); //直接解析请求到视图名
    initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现
    initFlashMapManager(context); //flash映射管理器
    }

    单个resolver
    initMultipartResolver,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator,initFlashMapManager 这五个初始化方法流程相同,都是使用
    context.getBean(String name, Class<FlashMapManager> requiredType)的方式获取到相应的Resolver。以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");
                }
            }
        }

    多个resolver

    initHandlerMappings,initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers 获取方式相同,使用:
    BeanFactoryUtils.beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<HandlerMapping> type, boolean includeNonSingletons, boolean allowEagerInit)
    的方式获取到相应的Resolver。以initHandlerMappings为例,见如下:
    /**
         * 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<HandlerMapping>(matchingBeans.values());
                    // We keep HandlerMappings in sorted order.
                    OrderComparator.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");
                }
            }
        }

    那么深入看一下BeanFactoryUtils.beansOfTypeIncludingAncestors 究竟做了什么?返回指定类型和子类型的所有bean,若该bean factory 是一个继承类型的beanFactory,这个方法也会获取祖宗factory中定义的指定类型的bean。

    /**
         * Return all beans of the given type or subtypes, also picking up beans defined in
         * ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
         * The returned Map will only contain beans of this type.
         * <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
         * which means that FactoryBeans will get initialized. If the object created by the
         * FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
         * type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
         * (which doesn't require initialization of each FactoryBean).
         * <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,
         * i.e. such beans will be returned from the lowest factory that they are being found in,
         * hiding corresponding beans in ancestor factories.</b> This feature allows for
         * 'replacing' beans by explicitly choosing the same bean name in a child factory;
         * the bean in the ancestor factory won't be visible then, not even for by-type lookups.
         * @param lbf the bean factory
         * @param type type of bean to match
         * @param includeNonSingletons whether to include prototype or scoped beans too
         * or just singletons (also applies to FactoryBeans)
         * @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
         * <i>objects created by FactoryBeans</i> (or by factory methods with a
         * "factory-bean" reference) for the type check. Note that FactoryBeans need to be
         * eagerly initialized to determine their type: So be aware that passing in "true"
         * for this flag will initialize FactoryBeans and "factory-bean" references.
         * @return the Map of matching bean instances, or an empty Map if none
         * @throws BeansException if a bean could not be created
         */
        public static <T> Map<String, T> beansOfTypeIncludingAncestors(
                ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
                throws BeansException {
    
            Assert.notNull(lbf, "ListableBeanFactory must not be null");
            Map<String, T> result = new LinkedHashMap<String, T>(4);
            result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
            if (lbf instanceof HierarchicalBeanFactory) {
                HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
                if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
                    Map<String, T> parentResult = beansOfTypeIncludingAncestors(
                            (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
                    for (Map.Entry<String, T> entry : parentResult.entrySet()) {
                        String beanName = entry.getKey();
                        if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
                            result.put(beanName, entry.getValue());
                        }
                    }
                }
            }
            return result;
        }

     2. 提供服务

         我们来看看这个servlet是如何提供服务的?

        @Override
        protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
            if (logger.isDebugEnabled()) {
                String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
                logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                        " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
            }
    
            // Keep a snapshot of the request attributes in case of an include,
            // to be able to restore the original attributes after the include.
            Map<String, Object> attributesSnapshot = null;
            if (WebUtils.isIncludeRequest(request)) {
                attributesSnapshot = new HashMap<String, Object>();
                Enumeration<?> attrNames = request.getAttributeNames();
                while (attrNames.hasMoreElements()) {
                    String attrName = (String) attrNames.nextElement();
                    if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
                        attributesSnapshot.put(attrName, request.getAttribute(attrName));
                    }
                }
            }
    
            // Make framework objects available to handlers and view objects.
            request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
            request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
            request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
            request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    
            try {
                doDispatch(request, response);
            }
            finally {
                if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                    return;
                }
                // Restore the original attribute snapshot, in case of an include.
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }

       从上面我们可以看到,提供服务只要分4步:

      1. 保存现场。保存request 熟悉的快照,以便能在必要时恢复。

      2. 将框架需要的对象放入request中,以便view和handler使用。

      3. 请求分发服务.

      4. 恢复现场。

    其中最重要的是请求分发服务:

        /**
         * Process the actual dispatching to the handler.
         * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
         * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
         * to find the first that supports the handler class.
         * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
         * themselves to decide which methods are acceptable.
         * @param request current HTTP request
         * @param response current HTTP response
         * @throws Exception in case of any kind of processing failure
         */
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;
    
            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;
    
                try {
                    processedRequest = checkMultipart(request);
                    multipartRequestParsed = (processedRequest != request);
    
                    // Determine handler for the current request.
                    mappedHandler = getHandler(processedRequest);
                    if (mappedHandler == null || mappedHandler.getHandler() == null) {
                        noHandlerFound(processedRequest, response);
                        return;
                    }
    
                    // Determine handler adapter for the current request.
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                    // Process last-modified header, if supported by the handler.
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                        if (logger.isDebugEnabled()) {
                            logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                        }
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
    
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
    
                    try {
                        // Actually invoke the handler.
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    }
                    finally {
                        if (asyncManager.isConcurrentHandlingStarted()) {
                            return;
                        }
                    }
    
                    applyDefaultViewName(request, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
                catch (Exception ex) {
                    dispatchException = ex;
                }
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            }
            catch (Exception ex) {
                triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
            }
            catch (Error err) {
                triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    // Instead of postHandle and afterCompletion
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                    return;
                }
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }

    分发过程如下:

    1. 判断是否设置了multipart resolver,设置的话转换为multipart request,没有的话则继续下面的步骤。

    2. 根据当前request,获取hangdler。

    3. 根据当前request,获取HandlerAdapter。

    4. 如果支持http请求头,处理 last-modified header请求头。

    5. 应用已注册interceptor的preHandle方法

    6. HandlerAdapter处理请求。

    7. 设置默认视图。

    8. 应用已注册interceptor的postHandle方法。

    9. 处理异常或者视图渲染。

    小结:

       DispatherServlet整个过程的细节一章之内很难描述的面面俱到,只能分析部分流程,想了解更具体的实现需要从源代码中去寻找。

  • 相关阅读:
    vue嵌套路由
    不同的网络出现的报错
    yarn 创建react项目时出现错误
    vue-awsome-swiper竖向滚动出现空白
    SpringBoot路径映射
    ApplicationRunner接口
    CommandLineRunner接口
    springboot创建拦截器
    Cors跨域请求
    springboot自定义异常视图
  • 原文地址:https://www.cnblogs.com/davidwang456/p/4096530.html
Copyright © 2011-2022 走看看