SpringBoot之DispatchServlet流程

前言

从DispatchServlet可以看出整个SpringMVC流程,关于过滤器(filter),servelet,拦截器(interceptor)的执行流程如下如

image-20211018153111255.png

使用到的组件

过滤器Filter:对请求信息进行校验,处理,例如判断是否有token,或者向请求添加请求头,请求参数等

新增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---请求添加请求数据userId");
            filterChain.doFilter(servletRequest,servletResponse);
        }
    }
    
  2. @Bean
    public FilterRegistrationBean myFilter(){
        final FilterRegistrationBean registrationBean = new FilterRegistrationBean<>();
        final MyFilter myFilter = new MyFilter();
        registrationBean.setFilter(myFilter);
        registrationBean.setUrlPatterns(Arrays.asList("/*"));
        registrationBean.setName("myFilter");
        return registrationBean;
    }
    

servlet:serlvet是处理请求的入口,DispatchServlet继承自Servlet,所有的请求都会走DispatchServlet,并且内部添加了对Rest风格的请求处理逻辑

添加普通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(){
        final ServletRegistrationBean 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("请求调用----:"+request.getRequestURL());
        super.doService(request, response);
    }
}

拦截器interceptor:在方法执行前后,结束时进行处理

新增拦截器的2种方式:

  1. @Configuration
    @EnableWebMvc // 这种方式会导致springSecurity的一些默认配置失效
    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());
        }
    }
    

    注意:当使用第一种@EnableWebMvc+WebMvcConfigurer方式进行实现时,会导致SpringSecurity的默认配置失效,建议使用第二种,或者两者结合使用,两者结合时,需要将@EnableWebMvc进行注释,这样Spring会将所有的配置进行合并

处理器映射HandlerMapping:将程序中定义的映射记录,会根据请求来判断是否使用某个映射,即根据请求获取到需要调用的方法,目前默认有三个,

image-20211018162721377.png

在添加了@RequestMapping和@GetMapping等等注解的方法,会被注册到HandlerMapping的mappingRegistry属性中,请求进来,会根据请求来匹配指定的方法

处理器适配器handlerAdapter:上一步找到了需要执行的处理器,处理器适配器就是用来执行处理器的,目前有4个

image-20211018172554141.png

方法参数处理器MethodArgumentResolver:方法的参数如何进行解析,例如实现@RequestBody和@RequestParam等注解的功能(案例:自定义@CurrentUser注解,通过解析请求头token来映射方法参数中的用户信息)

案例实现:

public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {
    /**
     * 该处理器是否能处理这个参数
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.hasParameterAnnotation(CurrentUser.class);
    }

    /**
     * 如何处理参数
     * @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 argumentResolvers) {
    // 注册参数处理器
        argumentResolvers.add(new CurrentUserMethodArgumentResolver());
    }


}

返回值处理器HandlerMethodReturnValueHandler:对于请求方法的返回值进行处理

创建返回值处理器的方式

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就是返回值,根据业务情况进行处理,例如对一个字段加减等等
    }
}
@Override
protected void addReturnValueHandlers(List returnValueHandlers) {
    returnValueHandlers.add(new MyReturnHandlerResolver());
}

响应体处理器RequestBodyAdvice:对于最后的数据进行处理,定义如何向response写出数据

实现RequestBodyAdvice的方式(可以多个,内部最终合并为一个RequestBodyAdviceChain,处理器链):

@ControllerAdvice
@ResponseBody
public class UnityExceptionHandler implements ResponseBodyAdvice {

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


    // 是否进行处理,根据自己的业务逻辑进行判断
    @Override
    public boolean supports(MethodParameter returnType, Class> converterType) {
        return true;
    }

    // 真正的处理逻辑,如果上一步返回true,这一步进行处理
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Response){
            return body;
        }else {
            return Response.success(body);
        }
    }
}
 
 

视图解析器ViewResolver:例如像thymeleaf,freemaker 都实现了该接口,如何进行视图的渲染

文件请求解析器MultipartResolver:如果属于文件上传类的接口会使用到该解析器

代码分析

DispatchServlet家族管理图如下

image-20211019103720149.png

调用链路为:Servlet.service()->GenericServlet.service()->HttpServlet.service()->FrameWorkServlet.doGet()/doPost()...->FrameWorkServlet.processRequest()->DispatcherServlet.doService()->DispatcherServlet.doDispatch() 不管get还是post或其他请求最终都会调用processRequest方法,然后调用doService方法,最后是doDispatch方法,所以重点在doDispatch方法的逻辑。

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 {
          // 是否文件上传请求的判断,如果是文件上传请求,将普通Request转换为MultipartHttpServletRequest对象
         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;
         }

         // 根据处理方法获取该处理方法的适配器
         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;
            }
         }

          // 调用拦截器链中的开始方法,查看【分析2】
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 使用适配器调用处理方法,返回ModelAndView对象作为处理结果,查看【分析4】
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
        // 根据返回的ModelAndView对象判断是否需要设置默认视图,查看【分析5】
         applyDefaultViewName(processedRequest, mv);
          // 调用所有拦截器的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);
      }
       // 根据modelAndView进行结果处理,查看【分析7】
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
       // 异常结束也会调用拦截器的完成方法,查看【分析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:最终调用AbstractHandlerMethodMapping.getHandlerInternal()方法

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 获取请求的路径
       String lookupPath = initLookupPath(request);
       this.mappingRegistry.acquireReadLock();
       try {
           // 通过请求路径从mappingRegistry获取出处理该请求的方法,所有的可以处理请求的方法在项目启动时都会注册到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);
           // 遍历所有的拦截器并调用preHandle方法
          if (!interceptor.preHandle(request, response, this.handler)) {
              // 如果其中一个方法处理并拦截掉了,不再执行后面的处理了,直接调用所有拦截器的完成方法
             triggerAfterCompletion(request, response, null);
             return false;
          }
          this.interceptorIndex = i;
       }
       return true;
    }
    
  3. 分析3:调用所有拦截器的处理完成方法,很多地方有调用,例如出现了异常,都会回调拦截器的处理完成方法,注意:反向遍历

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
       for (int i = this.interceptorIndex; i >= 0; i--) {
          HandlerInterceptor interceptor = this.interceptorList.get(i);
          try {
              // 调用所有拦截器的处理完成方法
             interceptor.afterCompletion(request, response, this.handler, ex);
          }
          catch (Throwable ex2) {
             logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
          }
       }
    }    
    
  4. 分析4:会调用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);
           // 获取所有的方法参数处理器(可以将请求传递的参数,封装为java对象等等)
          if (this.argumentResolvers != null) {
             invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
          }
           // 获取所有的返回结果处理器,(针对返回结果进行处理)
          if (this.returnValueHandlers != null) {
             invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
          }
          ......省略代码
             
             // 调用方法,并对参数和结果进行处理,返回结果放在mavConatiner里面,查看【分析4-1】
          invocableMethod.invokeAndHandle(webRequest, mavContainer);
          if (asyncManager.isConcurrentHandlingStarted()) {
             return null;
          }
         // 获取ModelAndView对象,查看【分析4-2】
          return getModelAndView(mavContainer, modelFactory, webRequest);
       }
       finally {
          webRequest.requestCompleted();
       }
    }
    
    • 分析4-1:

      public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
      
          // 反射调用方法,获取最原始的返回结果
         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;
         }
      
          // 这个值很重要,表示是否已经处理了请求,例如直接返回的json数据时值为true,因为在返回值处理器时已经将请求结果写入response,返回视图时,值为false
         mavContainer.setRequestHandled(false);
         Assert.state(this.returnValueHandlers != null, "No return value handlers");
         try {
             // 筛选出一个返回值处理器,使用返回值处理器处理返回结果,注意只有一个最合适的处理器进行处理,并且该处理器的处理方法中会根据自身情况选择设置mavContainer.setRequestHandled(false/true);表示自己是否处理返回结果,例如加了@ResponseBody的方法内部就会将值设置为true,并且调用所有的RequestBodyAdvice链再次处理请求结果,最终结果直接写入到Response,后续就不用再使用视图解析器了
            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对象进行返回

      @Nullable
      private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
            ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
      
         modelFactory.updateModel(webRequest, mavContainer);
          // 重点,根据这个标记值判断是否已经处理了返回请求,如果已经处理了,返回modelAndView为null,外面就不会再调用viewResoler进行视图解析了
         if (mavContainer.isRequestHandled()) {
            return null;
         }
          // 如果没有处理返回请求,使用ModelAndViewContainer封装一个ModelAndView进行返回
         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 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 {
        // 当mv不为空&&mv没有视图时,当我们自己的代码逻辑返回String值时,该ModeAndView的view就为返回值,hasView方法的值就为true,结束执行,但是当返回值为null,或者方法为void时,hasView方法会返回false,就会执行下面的方法,默认将请求路径作为视图
       if (mv != null && !mv.hasView()) {
          String defaultViewName = getDefaultViewName(request);
          if (defaultViewName != null) {
             mv.setViewName(defaultViewName);
          }
       }
    }
    
    //例如:
    /**
    * 如果请求/template/test  则mv.hasView = true,因为view = test
    * 如果请求/template/test2 则mv.hasView = false,因为view = null,然后执行getDefaultViewName,则mv.viewName = /template/test2(请求路径)
    * 请求/template/test3效果与请求/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:反向遍历所有拦截器,依次调用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;
     // 如果在执行自定义逻辑方法时出现异常会执行以下方法
       if (exception != null) {
          if (exception instanceof ModelAndViewDefiningException) {
             logger.debug("ModelAndViewDefiningException encountered", exception);
             mv = ((ModelAndViewDefiningException) exception).getModelAndView();
          }
          else {
             Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
              // 其他异常,调用异常处理器进行处理,查看【分析7-1】
             mv = processHandlerException(request, response, handler, exception);
             errorView = (mv != null);
          }
       }
    
       // 当mv不为空时才进行视图渲染,也可以看出,mavContainer.setRequestHandled(false);这个标志位控制了是否渲染视图
       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) {
          // 触发所有拦截器的完成方法,查看【分析3】
          mappedHandler.triggerAfterCompletion(request, response, null);
       }
    }
    
    • 分析7-1:异常处理器处理请求,例如可以使用@ControllerAdvice+@ExceptionHandler进行统一异常处理

      @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 {
         // 国际化信息
         Locale locale =
               (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
         response.setLocale(locale);
      
         View view;
         String viewName = mv.getViewName();
         if (viewName != null) {
            // 视图名称不为空时使用所有的视图解析器解析该名称生成view对象,查看【分析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请求等信息进行视图渲染,将结果写入到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 model,
              Locale locale, HttpServletRequest request) throws Exception {
        
           if (this.viewResolvers != null) {
               // 调用所有的视图解析器解析名称,获得view,所有的模板引擎也就是实现了viewResolver,使用自己的逻辑进行视图解析,例如freemaker默认解析路径为 classpath:/template下面后缀名为html的文件,例如viewName为test,则找到的文件则是classpath:/template/test.html,然后将该文件解析为view对象返回
              for (ViewResolver viewResolver : this.viewResolvers) {
                 View view = viewResolver.resolveViewName(viewName, locale);
                 if (view != null) {
                    return view;
                 }
              }
           }
           return null;
        }
        

你可能感兴趣的:(SpringBoot之DispatchServlet流程)