Spring MVC探秘之请求处理

  之前分析了Spring MVC的初始化过程,主要就是对WebApplicationContext和相关组件进行初始化。今天主要分析下Spring MVC处理请求的过程。
  初始化时主要分析了HttpServetBean、FrameworkServlet、DispatcherServlet三个类,在处理请求时,HttpServletBean并没有参与具体的处理过程,因此主要介绍FrameworkServlet和DispatcherServlet。

FrameworkServlet

processRequest

  在Servlet处理请求时,首先是从Servlet的service接口开始,然后在HttpServlet的service方法中根据请求的类型不同分别将请求路由到doGet, doPost, doPut, doHead, doDelete, doOptions和doTrace七个方法中,FrameworkServlet重载了httpServlet中的这几个方法(除了doHead)。在重载方法中,要不是交给父类处理,要不是交给自己处理,在交给自己处理时,和HttpServlet不同,FrameworkServlet将这些方法都交给了processRequest方法统一处理。以service和doGet为例:

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
   if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
      //转发到processRequest方法中
      processRequest(request, response);
   }
   else {
      //交给父类处理
      super.service(request, response);
   }
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   //转发到processRequest方法中
   processRequest(request, response);
}

  在service方法中,对于Patch类型的请求,交给自己处理,转发给processRequest方法,其他的请求交给父类处理。在doGet请求中,直接将请求转发给processRequest方法。
  下面具体分析下processRequest方法。

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      
   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
   //获取原先的localeContext并进行保存
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   //获取到当前请求的LocaleContext
   LocaleContext localeContext = buildLocaleContext(request);
   //获取原先的requestAttributes并进行保存
   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   //获取到当前请求的requestAttributes
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
   //拿到异步处理管理器并设置了拦截器
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
   
   //将localeContext和requestAttributes设置到LocaleContextHolder和RequestContextHolder中
   initContextHolders(request, localeContext, requestAttributes);
   try {
      //处理请求,DispatcherServlet具体实现
      doService(request, response);
   }
   catch (ServletException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (IOException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
   }
   finally {
      //恢复localeContext和requestAttributes
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }
      if (logger.isDebugEnabled()) {
         if (failureCause != null) {
            this.logger.debug("Could not complete request", failureCause);
         }
         else {
            if (asyncManager.isConcurrentHandlingStarted()) {
               logger.debug("Leaving response open for concurrent processing");
            }
            else {
               this.logger.debug("Successfully completed request");
            }
         }
      }
      //发出ServletRequestHandledEvent类型消息
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}

  在processRequest中,最主要的方法是doService方法,请求处理主要在这个方法里进行,DispatcherServlet实现了这个方法。与此同时,FrameworkServlet在doService前后还做了一些其他工作。在doService之前,其首先获取了LocaleContextHolder和RequestContextHolder中原来保存的LocaleContext和RequestAttributes并设置到了previousLocaleContext和previousAttributes临时属性,然后调用buildLocaleContext和buildRequestAttributes方法获取到当前请求的LocaleContext和RequestAttributes,并通过initContextHolders方法将它们设置到LocaleContextHolder和RequestContextHolder中,以供doService方法使用,接着使用request拿到异步处理管理器并设置了拦截器,做完这些后执行了doService方法,执行完后,最后(finally中)通过resetContextHolders方法将原来到previousLocaleContext和previousAttributes恢复到LocaleContextHolder和RequestContextHolder中,因为在Servlet之外可能还有其他的操作,如Filter等,为了不影响这些操作,需要进行恢复操作。在最后的最后,调用publishRequestHandledEvent方法发布了一个ServletRequestHandledEvent类型的消息,我们可以监听这个事件从而在处理完成后进行一些处理。

  • 注:虽然LocaleContextHolder和RequestContextHolder都是abstract类,调用的都是其static函数,但是这并不会产生并发问题,因为其中保存的属性都是ThreadLocal类型的对象,不同线程之间是独立的。
  • 注:LocaleContext里面存放着Locale(本地化信息,如zh-cn等),RequestAttributes是spring的一个接口,通过它可以get/set/removeAttributes类,在ServletRequestAttributes里面还封装了request\response和session,而且都提供了get方法,可以直接获取。简单来说,前者可以获取Locale,后者用于管理Request和Session的属性。

DisPatcherServlet

  DisPatcherServlet类是整个处理过程中最重要的类,下面对其中的处理过程进行具体分析。

@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(DEFAULT_STRATEGIES_PREFIX)) {
            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()) {
         // Restore the original attribute snapshot, in case of an include.
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}

  从上往下进行分析,首先当是include请求时,对request的Attribute属性做快照备份,在处理完之后进行还原。然后对request设置一些属性,前四个属性WEB_APPLICATION_CONTEXT_ATTRIBUTE、LOCALE_RESOLVER_ATTRIBUTE、THEME_RESOLVER_ATTRIBUTE、THEME_SOURCE_ATTRIBUTE在doDispatch中会使用到,之后再做具体分析。后三个属性都和flashMap有关,主要用于Redirect转发时参数的传递,关于flashMap的具体功能是使用,详见文章:Spring MVC Flash Attribute 的讲解与使用示例。这边就不做具体介绍了。
  下面具体分析最重要的方法doDispatch。

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;
         }
         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         applyDefaultViewName(processedRequest, mv);
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarios.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

  doDispatch方法的逻辑也比较清晰,其中主要做了四个工作:

//根据request请求找到HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
//根据handler找到HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//HandlerAdapter进行请求的处理,并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//调用processDispatchResult方法处理上面处理之后的结果,包括渲染并输出ModelAndView
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

  这边首先介绍三个概念,HandlerMapping,Handler和HandlerAdapter。

  • HandlerMapping:是用来查找Handler的,在一个Spring MVC中会出现很多请求,如何根据不同的请求找到对应的Handler便是HandlerMappings的工作。
  • Handler:处理器,它和Controller是对应的,它的具体表现形式可以是类也可以是方法,在Controller层中标注了@RequestMapping注解的方法都可以是一个Handler。
  • HandlerAdapter:从字面意义上可以知道,这是针对Handler的一个适配器,因为Handler有各种不同的形式,但是Servlet中执行Handler的结构却是不变的,都是以request和response为参数的方法,HandlerAdapter作为中间的适配器连接了Servlet和Handler。

  此外View和ViewResolver的关系和Handler和HandlerMapping的关系类似,View是用来展示数据的,而ViewResolver用来查找View。
  总结下,上面四行关键代码表达的意思是,使用HandlerMapping找到Handler,再根据Handler生成其对应的HandlerAdapter,让HandlerAdapter调用Handler处理请求,在将处理结果(View)展示给用户。
  接下来具体介绍doDispatch方法的具体处理流程。
  doDispatch方法首先会检查是不是上传的请求,如果是上传请求会将request转为MultipartHttpServletRequest,并将标志multipartRequestParsed置为true,其中使用到了MultipartResolver。
  然后通过getHandler方法获取HandlerExecutionChain,执行链中包括了Interceptor和Handler。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
         logger.trace(
               "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = getApplicationContext().getBean(handlerName);
   }
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}

  getHandler方法比较简单,它会遍历所有的HandlerMapping,如果哪个HandlerMapping有对应的Handler便返回对应的Handler,此时若是返回的handler为字符串类型,则说明此时返回的handler为bean的名称,通过ApplicationContext.getBean()方法获取具体的handler。之后会根据handler会生成对应的HandlerExecutionChain。HandlerExecutionChain由Intercepters和Handler组成,其最主要的目的是将配置中的Intercepters加到请求的执行链中,执行时先执行Intercepters的preHandle方法,之后执行handle方法,最后执行Intercepters的postHandle方法。每一个请求按理说都应该对应着一个handler,一旦遇到找不到对应的handler的情况,开发人员可以设置一个默认的handler,如果连默认的handler也没有,此时直接返回null。下面看下查找具体handler的方法getHandlerInternal的代码。

@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   //截取用户匹配的url的有效路径
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   //根据url找到相应的handler
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {
      // We need to care for the default handler directly, since we need to
      // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         //返回rootHandler
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         //如果没有能够匹配的handler,返回默认的handler
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {
            //如果handler是String,那么去获取相应的bean
            String handlerName = (String) rawHandler;
            rawHandler = getApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
   }
   else if (handler == null && logger.isTraceEnabled()) {
      logger.trace("No handler mapping found for [" + lookupPath + "]");
   }
   return handler;
}
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // Direct match?
   //根据url直接匹配到了相应的handler
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
         //如果handler是String,那么去获取相应的bean
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }

   // Pattern match?
   //根据url未能够完全匹配到handler,此时根据通配符正则匹配handler
   List<String> matchingPatterns = new ArrayList<String>();
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      else if (useTrailingSlashMatch()) {
         if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
            matchingPatterns.add(registeredPattern +"/");
         }
      }
   }

   String bestMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      Collections.sort(matchingPatterns, patternComparator);
      if (logger.isDebugEnabled()) {
         logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestMatch = matchingPatterns.get(0);
   }
   if (bestMatch != null) {
      handler = this.handlerMap.get(bestMatch);
      if (handler == null) {
         if (bestMatch.endsWith("/")) {
            handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
         }
         if (handler == null) {
            throw new IllegalStateException(
                  "Could not find handler for best pattern match [" + bestMatch + "]");
         }
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         //如果handler是String,那么去获取相应的bean
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

      // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
      // for all of them
      Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      if (logger.isDebugEnabled()) {
         logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

   // No handler found...
   return null;
}

  虽然上面的代码看着很多,但总结下来主要就做了这些事,首先获取用来匹配handler的有效url,如果直接根据url完全匹配获取url最好,不能的话,也可以根据通配符去获取相应的handler。此时如果还没有找到对应的handler,url为"/",那么就返回rootHandler,最后实在找不到handler时,返回defaultHandler。
  在根据handler生成HandlerExecutionChain时,主要是根据将”url”,对应的拦截器加入HandlerExecutionChain中。
  回到DispatcherServlet中,下面的操作是根据handler生成对应的HandlerAdapter。在默认情况下普通的web请求会交给SimpleControllerHandlerAdapter去处理,下面就分析一下获取它的逻辑。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   for (HandlerAdapter ha : this.handlerAdapters) {
      if (logger.isTraceEnabled()) {
         logger.trace("Testing handler adapter [" + ha + "]");
      }
      if (ha.supports(handler)) {
         return ha;
      }
   }
   throw new ServletException("No adapter for handler [" + handler +
         "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

  可以看出根据handler去获取handlerAdapter的逻辑比较简单,就是循环遍历所有HandlerAdapter,如果有那个HandlerAdapter能够支持handler,那么便返回它,下面是SimpleControllerHandlerAdapter的support重写方法。

@Override
public boolean supports(Object handler) {
   return (handler instanceof Controller);
}

  看到这里,就能够明白SimpleControllerHandlerAdapter就是用来处理继承了Controller接口的请求,对于Spring MVC来说,我们会把逻辑封装到Controller的子类中。其他几种HandlerAdapter之后在具体讲HandlerAdapter详细说明。
  接下来是处理GET和HEAD请求的last-Modified,这个主要是为了利用缓存。服务器会记录上次对资源修改的时间,如果这次接收到带有last-Modified的请求后会将其值和上次修改的时间想比较,如果这段时间内尚未修改资源便可以直接返回304表示资源未过期,浏览器直接使用上次缓存的结果。
  之后是一次调用Intercepter的preHandle方法,其中每个intercepter都可以终止请求的处理过程。处理完preHandle方法后便来到最主要的地方,利用handlerAdapter调用handler进行请求的具体处理,也就是执行我们写在controller层的处理逻辑。下面以常见的RequestMappingHandlerAdapter为例介绍handle的处理流程,RequestMappingHandlerAdapter继承了AbstractHandlerMethodAdapter,handle方式是在AbstractHandlerMethodAdapter中实现的。

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
   return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
   ModelAndView mav;
   //对请求做校验,是否支持此请求,如果handler需要session,请求中是否包含session
   checkRequest(request);
   // Execute invokeHandlerMethod in synchronized block if required.
   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
         // No HttpSession available -> no mutex necessary
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
      // No synchronization on session demanded at all...
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }
   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }
   return mav;
}

  从handleInternal中可以看出,有两种请求处理方式,如果synchronizeOnSession标志位true,则对session进行同步处理,否则直接无需同步,直接处理即可。最后调用invokeHandlerMethod进行具体的请求处理。invokeHandlerMethod方法的在此不做具体分析。
  回到DispatcherServlet中,在调用HandlerAdapter.handle方法后,如果需要进行异步处理则直接返回,当View为空时,这是默认View(applyDefaultViewName),然后执行intercepter的postHandle方法。到此为止对于一个请求的处理(自己定义的那部分)便完成了。接下来需要调用processDispatchResult方法处理请求返回的结果,包括处理异常、渲染页面、触发intercepter的afterCompletion三部分。
  从代码中可以看出,doDispatch方法中的捕捉了两层异常,内层主要是捕获处理请求时的异常,然后将其转为dispatchException进入外层,外层主要是处理processDispatchResult会出现的异常。processDispatchResult的代码如下:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
   boolean errorView = false;
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }
   // Did the handler return a view to render?
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isDebugEnabled()) {
         logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
               "': assuming HandlerAdapter completed request handling");
      }
   }
   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Concurrent handling started during a forward
      return;
   }
   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

  可以看出,当处理请求时出现异常是,Spring MVC的做法是将错误页面赋给mv。如果前面处理正常,则调用render方法进行页面的渲染。render中首先对response设置了Locale,过程中使用了LocalResolver,然后判断如果view是String类型则调用resolveViewName方式使用ViewResolver获取实际的view,最后调用view的render方法对页面进行具体的渲染,渲染的过程中使用了ThemeResolver。
  最后通过mappedHandler的triggerAfterCompletion方法触发Intercepter的afterCompletion方法,这里的intercepter也是按照反方向执行的,到此为止processDispatchResult便执行完成了。
  再回到doDispatch方法中在最后的finally方法中判断请求是否启动了异步处理如果启动了则调用相应异步处理的拦截器,否则判断是否是上传请求,如果是上传请求则清除处理上传请求过程中产生的临时资源。
  到此为止Spring MVC对一个请求整个的处理过程便全部完成了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值