SpringBoot之DispatchServlet流程

前言

從DispatchServlet可以看出整個(gè)SpringMVC流程十艾,關(guān)于過濾器(filter),servelet,攔截器(interceptor)的執(zhí)行流程如下如

image-20211018153111255.png

使用到的組件

過濾器Filter:對(duì)請(qǐng)求信息進(jìn)行校驗(yàn),處理,例如判斷是否有token存璃,或者向請(qǐng)求添加請(qǐng)求頭,請(qǐng)求參數(shù)等

新增filter的兩種方式:

  1. @WebFilter(urlPatterns = "/*",filterName = "myFilter") // 指定filter的處理路徑和雕拼,filter名稱
    @Component
    public class MyFilter implements Filter {
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            servletRequest.setAttribute("userId","zhangsan");
            MDC.put("traceId", IdUtil.simpleUUID());
            //log.error("過濾器doFilter---請(qǐng)求添加請(qǐng)求數(shù)據(jù)userId");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    
  2. @Bean
    public FilterRegistrationBean<MyFilter> myFilter(){
        final FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>();
        final MyFilter myFilter = new MyFilter();
        registrationBean.setFilter(myFilter);
        registrationBean.setUrlPatterns(Arrays.asList("/*"));
        registrationBean.setName("myFilter");
        return registrationBean;
    }
    

servlet:serlvet是處理請(qǐng)求的入口纵东,DispatchServlet繼承自Servlet,所有的請(qǐng)求都會(huì)走DispatchServlet啥寇,并且內(nèi)部添加了對(duì)Rest風(fēng)格的請(qǐng)求處理邏輯

添加普通servlet的兩種方式

  1. @WebServlet(urlPatterns = "/*",name = "myServlet")
    @Component
    public class MyServlet extends HttpServlet {
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doGet(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doPost(req, resp);
        }
    
        @Override
        protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            super.doDelete(req, resp);
        }
    }
    
  2. @Bean
    public ServletRegistrationBean<MyServlet> myServlet(){
        final ServletRegistrationBean<MyServlet> registrationBean = new ServletRegistrationBean<>();
        final MyServlet myServlet = new MyServlet();
        registrationBean.setServlet(myServlet);
        registrationBean.setUrlMappings(Arrays.asList("/*"));
        registrationBean.setName("myServlet");
        return registrationBean;
    }
    

自定義DIspatchServlet:

@Component("dispatcherServlet")
public class MyDispatcherServlet extends DispatcherServlet {
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.error("請(qǐng)求調(diào)用----:"+request.getRequestURL());
        super.doService(request, response);
    }
}

攔截器interceptor:在方法執(zhí)行前后偎球,結(jié)束時(shí)進(jìn)行處理

新增攔截器的2種方式:

  1. @Configuration
    @EnableWebMvc // 這種方式會(huì)導(dǎo)致springSecurity的一些默認(rèn)配置失效
    public class WebMvcConfigByConfigure implements WebMvcConfigurer{
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor());
        }
     }
    
  2. @Configuration
    public class WebMvcConfigBySupport extends DelegatingWebMvcConfiguration {
    
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor());
        }
    }
    

    注意:當(dāng)使用第一種@EnableWebMvc+WebMvcConfigurer方式進(jìn)行實(shí)現(xiàn)時(shí),會(huì)導(dǎo)致SpringSecurity的默認(rèn)配置失效辑甜,建議使用第二種衰絮,或者兩者結(jié)合使用,兩者結(jié)合時(shí)磷醋,需要將@EnableWebMvc進(jìn)行注釋猫牡,這樣Spring會(huì)將所有的配置進(jìn)行合并

處理器映射HandlerMapping:將程序中定義的映射記錄,會(huì)根據(jù)請(qǐng)求來判斷是否使用某個(gè)映射邓线,即根據(jù)請(qǐng)求獲取到需要調(diào)用的方法淌友,目前默認(rèn)有三個(gè)煌恢,

image-20211018162721377.png

在添加了@RequestMapping和@GetMapping等等注解的方法,會(huì)被注冊(cè)到HandlerMapping的mappingRegistry屬性中震庭,請(qǐng)求進(jìn)來瑰抵,會(huì)根據(jù)請(qǐng)求來匹配指定的方法

處理器適配器handlerAdapter:上一步找到了需要執(zhí)行的處理器,處理器適配器就是用來執(zhí)行處理器的器联,目前有4個(gè)

image-20211018172554141.png

方法參數(shù)處理器MethodArgumentResolver:方法的參數(shù)如何進(jìn)行解析二汛,例如實(shí)現(xiàn)@RequestBody和@RequestParam等注解的功能(案例:自定義@CurrentUser注解,通過解析請(qǐng)求頭token來映射方法參數(shù)中的用戶信息)

案例實(shí)現(xiàn):

public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
    /**
     * 該處理器是否能處理這個(gè)參數(shù)
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(CurrentUser.class);
    }

    /**
     * 如何處理參數(shù)
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        final String token = nativeWebRequest.getHeader("token");
        SysUser user= UserService.getUserFromToken(token);
        return user;
    }
}
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    // 注冊(cè)參數(shù)處理器
        argumentResolvers.add(new CurrentUserMethodArgumentResolver());
    }


}

返回值處理器HandlerMethodReturnValueHandler:對(duì)于請(qǐng)求方法的返回值進(jìn)行處理

創(chuàng)建返回值處理器的方式

public class MyReturnHandlerResolver implements HandlerMethodReturnValueHandler {
    @Override
    public boolean supportsReturnType(MethodParameter methodParameter) {
        return methodParameter.hasMethodAnnotation(ParseReturn.class);
    }

    @Override
    public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception {
        //o就是返回值拨拓,根據(jù)業(yè)務(wù)情況進(jìn)行處理肴颊,例如對(duì)一個(gè)字段加減等等
    }
}
@Override
protected void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
    returnValueHandlers.add(new MyReturnHandlerResolver());
}

響應(yīng)體處理器RequestBodyAdvice:對(duì)于最后的數(shù)據(jù)進(jìn)行處理,定義如何向response寫出數(shù)據(jù)

實(shí)現(xiàn)RequestBodyAdvice的方式(可以多個(gè)渣磷,內(nèi)部最終合并為一個(gè)RequestBodyAdviceChain苫昌,處理器鏈):

@ControllerAdvice
@ResponseBody
public class UnityExceptionHandler implements ResponseBodyAdvice<Object> {

    @ExceptionHandler(Exception.class)
    public Response<String> exception(Exception e){
        return Response.fail(e.getMessage());
    }


    // 是否進(jìn)行處理,根據(jù)自己的業(yè)務(wù)邏輯進(jìn)行判斷
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    // 真正的處理邏輯幸海,如果上一步返回true,這一步進(jìn)行處理
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Response){
            return body;
        }else {
            return Response.success(body);
        }
    }
}

視圖解析器ViewResolver:例如像thymeleaf奥务,freemaker 都實(shí)現(xiàn)了該接口物独,如何進(jìn)行視圖的渲染

文件請(qǐng)求解析器MultipartResolver:如果屬于文件上傳類的接口會(huì)使用到該解析器

代碼分析

DispatchServlet家族管理圖如下

image-20211019103720149.png

調(diào)用鏈路為:Servlet.service()->GenericServlet.service()->HttpServlet.service()->FrameWorkServlet.doGet()/doPost()...->FrameWorkServlet.processRequest()->DispatcherServlet.doService()->DispatcherServlet.doDispatch() 不管get還是post或其他請(qǐng)求最終都會(huì)調(diào)用processRequest方法,然后調(diào)用doService方法氯葬,最后是doDispatch方法挡篓,所以重點(diǎn)在doDispatch方法的邏輯。

doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   // 是否為文件上傳的請(qǐng)求標(biāo)識(shí)
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
          // 是否文件上傳請(qǐng)求的判斷帚称,如果是文件上傳請(qǐng)求官研,將普通Request轉(zhuǎn)換為MultipartHttpServletRequest對(duì)象
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // 通過HandlerMapping獲取處理方法,查看【分析1】.
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
             // 如果沒有處理的方法闯睹,返回異常戏羽,也就是我們看見的No mapping for GET '/user/get/123'
            noHandlerFound(processedRequest, response);
            return;
         }

         // 根據(jù)處理方法獲取該處理方法的適配器
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = HttpMethod.GET.matches(method);
         if (isGet || HttpMethod.HEAD.matches(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }

          // 調(diào)用攔截器鏈中的開始方法,查看【分析2】
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 使用適配器調(diào)用處理方法楼吃,返回ModelAndView對(duì)象作為處理結(jié)果始花,查看【分析4】
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
        // 根據(jù)返回的ModelAndView對(duì)象判斷是否需要設(shè)置默認(rèn)視圖,查看【分析5】
         applyDefaultViewName(processedRequest, mv);
          // 調(diào)用所有攔截器的postHandle方法孩锡,查看【分析6】
         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);
      }
       // 根據(jù)modelAndView進(jìn)行結(jié)果處理酷宵,查看【分析7】
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
       // 異常結(jié)束也會(huì)調(diào)用攔截器的完成方法,查看【分析3】
      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);
         }
      }
   }
}
  1. 分析1:最終調(diào)用AbstractHandlerMethodMapping.getHandlerInternal()方法

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 獲取請(qǐng)求的路徑
       String lookupPath = initLookupPath(request);
       this.mappingRegistry.acquireReadLock();
       try {
           // 通過請(qǐng)求路徑從mappingRegistry獲取出處理該請(qǐng)求的方法躬窜,所有的可以處理請(qǐng)求的方法在項(xiàng)目啟動(dòng)時(shí)都會(huì)注冊(cè)到mappingRegistry
          HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
          return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
       }
       finally {
          this.mappingRegistry.releaseReadLock();
       }
    }
    
  2. 分析2:

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
       for (int i = 0; i < this.interceptorList.size(); i++) {
          HandlerInterceptor interceptor = this.interceptorList.get(i);
           // 遍歷所有的攔截器并調(diào)用preHandle方法
          if (!interceptor.preHandle(request, response, this.handler)) {
              // 如果其中一個(gè)方法處理并攔截掉了浇垦,不再執(zhí)行后面的處理了,直接調(diào)用所有攔截器的完成方法
             triggerAfterCompletion(request, response, null);
             return false;
          }
          this.interceptorIndex = i;
       }
       return true;
    }
    
  3. 分析3:調(diào)用所有攔截器的處理完成方法荣挨,很多地方有調(diào)用男韧,例如出現(xiàn)了異常朴摊,都會(huì)回調(diào)攔截器的處理完成方法,注意:反向遍歷

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
       for (int i = this.interceptorIndex; i >= 0; i--) {
          HandlerInterceptor interceptor = this.interceptorList.get(i);
          try {
              // 調(diào)用所有攔截器的處理完成方法
             interceptor.afterCompletion(request, response, this.handler, ex);
          }
          catch (Throwable ex2) {
             logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
          }
       }
    }    
    
  4. 分析4:會(huì)調(diào)用RequestMappingHandlerAdapter.invokeHandlerMethod()方法

    @Nullable
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
       ServletWebRequest webRequest = new ServletWebRequest(request, response);
       try {
          WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
          ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
          ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
           // 獲取所有的方法參數(shù)處理器(可以將請(qǐng)求傳遞的參數(shù)煌抒,封裝為java對(duì)象等等)
          if (this.argumentResolvers != null) {
             invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
          }
           // 獲取所有的返回結(jié)果處理器仍劈,(針對(duì)返回結(jié)果進(jìn)行處理)
          if (this.returnValueHandlers != null) {
             invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
          }
          ......省略代碼
             
             // 調(diào)用方法,并對(duì)參數(shù)和結(jié)果進(jìn)行處理寡壮,返回結(jié)果放在mavConatiner里面贩疙,查看【分析4-1】
          invocableMethod.invokeAndHandle(webRequest, mavContainer);
          if (asyncManager.isConcurrentHandlingStarted()) {
             return null;
          }
         // 獲取ModelAndView對(duì)象,查看【分析4-2】
          return getModelAndView(mavContainer, modelFactory, webRequest);
       }
       finally {
          webRequest.requestCompleted();
       }
    }
    
    • 分析4-1:

      public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
      
          // 反射調(diào)用方法况既,獲取最原始的返回結(jié)果
         Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
         setResponseStatus(webRequest);
      
         if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
               disableContentCachingIfNecessary(webRequest);
               mavContainer.setRequestHandled(true);
               return;
            }
         }
         else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
         }
      
          // 這個(gè)值很重要这溅,表示是否已經(jīng)處理了請(qǐng)求,例如直接返回的json數(shù)據(jù)時(shí)值為true棒仍,因?yàn)樵诜祷刂堤幚砥鲿r(shí)已經(jīng)將請(qǐng)求結(jié)果寫入response悲靴,返回視圖時(shí),值為false
         mavContainer.setRequestHandled(false);
         Assert.state(this.returnValueHandlers != null, "No return value handlers");
         try {
             // 篩選出一個(gè)返回值處理器莫其,使用返回值處理器處理返回結(jié)果癞尚,注意只有一個(gè)最合適的處理器進(jìn)行處理,并且該處理器的處理方法中會(huì)根據(jù)自身情況選擇設(shè)置mavContainer.setRequestHandled(false/true);表示自己是否處理返回結(jié)果乱陡,例如加了@ResponseBody的方法內(nèi)部就會(huì)將值設(shè)置為true浇揩,并且調(diào)用所有的RequestBodyAdvice鏈再次處理請(qǐng)求結(jié)果,最終結(jié)果直接寫入到Response憨颠,后續(xù)就不用再使用視圖解析器了
            this.returnValueHandlers.handleReturnValue(
                  returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
         }
         catch (Exception ex) {
            if (logger.isTraceEnabled()) {
               logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
         }
      }
      
    • 分析4-2:將上一步的ModelAndViewContainer容器中獲取modelAndVIew對(duì)象進(jìn)行返回

      @Nullable
      private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
      
         modelFactory.updateModel(webRequest, mavContainer);
          // 重點(diǎn)胳徽,根據(jù)這個(gè)標(biāo)記值判斷是否已經(jīng)處理了返回請(qǐng)求,如果已經(jīng)處理了爽彤,返回modelAndView為null养盗,外面就不會(huì)再調(diào)用viewResoler進(jìn)行視圖解析了
         if (mavContainer.isRequestHandled()) {
            return null;
         }
          // 如果沒有處理返回請(qǐng)求,使用ModelAndViewContainer封裝一個(gè)ModelAndView進(jìn)行返回
         ModelMap model = mavContainer.getModel();
         ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
         if (!mavContainer.isViewReference()) {
            mav.setView((View) mavContainer.getView());
         }
         if (model instanceof RedirectAttributes) {
            Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            if (request != null) {
               RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
            }
         }
         return mav;
      }
      
  5. 分析5:

    private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
        // 當(dāng)mv不為空&&mv沒有視圖時(shí)适篙,當(dāng)我們自己的代碼邏輯返回String值時(shí)往核,該ModeAndView的view就為返回值,hasView方法的值就為true嚷节,結(jié)束執(zhí)行铆铆,但是當(dāng)返回值為null,或者方法為void時(shí)丹喻,hasView方法會(huì)返回false薄货,就會(huì)執(zhí)行下面的方法,默認(rèn)將請(qǐng)求路徑作為視圖
       if (mv != null && !mv.hasView()) {
          String defaultViewName = getDefaultViewName(request);
          if (defaultViewName != null) {
             mv.setViewName(defaultViewName);
          }
       }
    }
    
    //例如:
    /**
    * 如果請(qǐng)求/template/test  則mv.hasView = true碍论,因?yàn)関iew = test
    * 如果請(qǐng)求/template/test2 則mv.hasView = false,因?yàn)関iew = null,然后執(zhí)行g(shù)etDefaultViewName谅猾,則mv.viewName = /template/test2(請(qǐng)求路徑)
    * 請(qǐng)求/template/test3效果與請(qǐng)求/template/test2效果一致
    **/
    @Controller
    @RequestMapping("/template")
    public class TemplateController {
    
        @GetMapping("/test")
        public String test(ModelAndView mv){
            mv.addObject("name","張三");
            mv.addObject("age",18);
            return "test";
        }
        @GetMapping("/test2")
        public String test2(ModelAndView mv){
            mv.addObject("name","張三");
            mv.addObject("age",18);
            return null;
        }
        @GetMapping("/test3")
        public void test3(ModelAndView mv){
            mv.addObject("name","張三");
            mv.addObject("age",18);
        }
    }
    
  6. 分析6:反向遍歷所有攔截器,依次調(diào)用postHandle方法

    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
       HandlerInterceptor interceptor = this.interceptorList.get(i);
       interceptor.postHandle(request, response, this.handler, mv);
    }
    
  7. 分析7:處理異常和視圖解析

    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
          @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
          @Nullable Exception exception) throws Exception {
    
       boolean errorView = false;
     // 如果在執(zhí)行自定義邏輯方法時(shí)出現(xiàn)異常會(huì)執(zhí)行以下方法
       if (exception != null) {
          if (exception instanceof ModelAndViewDefiningException) {
             logger.debug("ModelAndViewDefiningException encountered", exception);
             mv = ((ModelAndViewDefiningException) exception).getModelAndView();
          }
          else {
             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
              // 其他異常,調(diào)用異常處理器進(jìn)行處理税娜,查看【分析7-1】
             mv = processHandlerException(request, response, handler, exception);
             errorView = (mv != null);
          }
       }
    
       // 當(dāng)mv不為空時(shí)才進(jìn)行視圖渲染坐搔,也可以看出,mavContainer.setRequestHandled(false);這個(gè)標(biāo)志位控制了是否渲染視圖
       if (mv != null && !mv.wasCleared()) {
           // 渲染視圖敬矩,查看【分析7-2】
          render(mv, request, response);
          if (errorView) {
             WebUtils.clearErrorRequestAttributes(request);
          }
       }
       else {
          if (logger.isTraceEnabled()) {
             logger.trace("No view rendering, null ModelAndView returned.");
          }
       }
    
       if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
          // Concurrent handling started during a forward
          return;
       }
    
       if (mappedHandler != null) {
          // 觸發(fā)所有攔截器的完成方法概行,查看【分析3】
          mappedHandler.triggerAfterCompletion(request, response, null);
       }
    }
    
    • 分析7-1:異常處理器處理請(qǐng)求,例如可以使用@ControllerAdvice+@ExceptionHandler進(jìn)行統(tǒng)一異常處理

      @ControllerAdvice
      @ResponseBody
      public class MyExceptionHandler {
          @ExceptionHandler(RuntimeException.class)
          public String runtime(Exception e){
              return e.getMessage();
          }
      }
      
    • 分析7-2:

      protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
         // 國(guó)際化信息
         Locale locale =
               (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
         response.setLocale(locale);
      
         View view;
         String viewName = mv.getViewName();
         if (viewName != null) {
            // 視圖名稱不為空時(shí)使用所有的視圖解析器解析該名稱生成view對(duì)象弧岳,查看【分析7-2-1】
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
            if (view == null) {
               throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
                     "' in servlet with name '" + getServletName() + "'");
            }
         }
         else {
            // 如果視圖名為空凳忙,則查看modelAndView中的view是否為空,不為空則返回禽炬,為空異常
            view = mv.getView();
            if (view == null) {
               throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
                     "View object in servlet with name '" + getServletName() + "'");
            }
         }
      
         // Delegate to the View object for rendering.
         if (logger.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
         }
         try {
            if (mv.getStatus() != null) {
               response.setStatus(mv.getStatus().value());
            }
             // 使用view和mv請(qǐng)求等信息進(jìn)行視圖渲染涧卵,將結(jié)果寫入到response
            view.render(mv.getModelInternal(), request, response);
         }
         catch (Exception ex) {
            if (logger.isDebugEnabled()) {
               logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
         }
      }
      
      • 分析7-2-1:

        @Nullable
        protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
              Locale locale, HttpServletRequest request) throws Exception {
        
           if (this.viewResolvers != null) {
               // 調(diào)用所有的視圖解析器解析名稱,獲得view腹尖,所有的模板引擎也就是實(shí)現(xiàn)了viewResolver柳恐,使用自己的邏輯進(jìn)行視圖解析,例如freemaker默認(rèn)解析路徑為 classpath:/template下面后綴名為html的文件热幔,例如viewName為test,則找到的文件則是classpath:/template/test.html,然后將該文件解析為view對(duì)象返回
              for (ViewResolver viewResolver : this.viewResolvers) {
                 View view = viewResolver.resolveViewName(viewName, locale);
                 if (view != null) {
                    return view;
                 }
              }
           }
           return null;
        }
        
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乐设,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子绎巨,更是在濱河造成了極大的恐慌近尚,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件认烁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡介汹,警方通過查閱死者的電腦和手機(jī)却嗡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嘹承,“玉大人窗价,你說我怎么就攤上這事√揪恚” “怎么了撼港?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)骤竹。 經(jīng)常有香客問我帝牡,道長(zhǎng),這世上最難降的妖魔是什么蒙揣? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任靶溜,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘罩息。我一直安慰自己嗤详,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布瓷炮。 她就那樣靜靜地躺著葱色,像睡著了一般。 火紅的嫁衣襯著肌膚如雪娘香。 梳的紋絲不亂的頭發(fā)上苍狰,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音茅主,去河邊找鬼舞痰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛诀姚,可吹牛的內(nèi)容都是我干的响牛。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赫段,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼呀打!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起糯笙,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤贬丛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后给涕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豺憔,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年够庙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恭应。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耘眨,死狀恐怖昼榛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剔难,我是刑警寧澤胆屿,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站偶宫,受9級(jí)特大地震影響非迹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纯趋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一彻秆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦唇兑、人聲如沸酒朵。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔫耽。三九已至,卻和暖如春留夜,著一層夾襖步出監(jiān)牢的瞬間匙铡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工碍粥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳖眼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓嚼摩,卻偏偏與公主長(zhǎng)得像钦讳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子枕面,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容