拦截器

1、拦截器简介

  • Spring MVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    throws Exception {

    return true;
    }

    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
    @Nullable ModelAndView modelAndView) throws Exception {
    }

    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
    @Nullable Exception ex) throws Exception {
    }

    }
  • preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
  • postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。(目标方法调用后)
  • afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。(资源响应之后)

2、单拦截器运行流程

  • ①新建一个https://perfectcode.top/2020/12/20/SpringMVC%E8%BF%90%E8%A1%8C%E6%B5%81%E7%A8%8B/的测试用例,在此基础上新建一个自定义拦截器MyFirstInterceptor:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class MyFirstInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("MyFirstInterceptor-->preHandle运行");
    return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("MyFirstInterceptor-->postHandle运行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("MyFirstInterceptor-->afterCompletion运行");
    }
    }
  • ②在springmvc.xml中将自定义拦截器加到SpringMVC的拦截体系中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--配置拦截器-->
    <mvc:interceptors>
    <!--默认拦截所有请求-->
    <!--<bean class="com.example.springmvc.config.MyFirstInterceptor"></bean>-->

    <!--具体配置某个拦截器-->
    <mvc:interceptor>
    <mvc:mapping path="/hello"/>
    <bean class="com.example.config.MyFirstInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>
  • ③启动项目,并请求/hello接口,控制台打印如下:

3、多拦截器运行流程

  • ①在前面的基础上,新建第二个自定义拦截器MySecondInterceptor:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class MySecondInterceptor implements HandlerInterceptor {
    //目标方法运行前运行,return false为不放行,如果不放行则没有后面的流程。
    //只要放行了,afterCompletion都会执行。
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("MySecondInterceptor-->preHandle运行");
    return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("MySecondInterceptor-->postHandle运行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("MySecondInterceptor-->afterCompletion运行");
    }
    }
  • ②在springmvc.xml中将自定义拦截器加到SpringMVC的拦截体系中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!--配置拦截器-->
    <mvc:interceptors>
    <!--默认拦截所有请求-->
    <!--<bean class="com.example.springmvc.config.MyFirstInterceptor"></bean>-->

    <!--具体配置某个拦截器-->
    <!--两个拦截器谁先配置谁就先运行-->
    <mvc:interceptor>
    <mvc:mapping path="/hello"/>
    <bean class="com.example.config.MyFirstInterceptor"></bean>
    </mvc:interceptor>

    <mvc:interceptor>
    <mvc:mapping path="/hello"/>
    <bean class="com.example.config.MySecondInterceptor"></bean>
    </mvc:interceptor>
    </mvc:interceptors>
  • ③启动项目,并请求/hello接口,控制台打印如下:

  • 注意:

    • ①任何一个拦截器不放行(preHandle方法返回false),目标方法都不会执行。
    • ②已经放行了的拦截器的afterCompletion方法一定会执行。
    • ③多个拦截器的preHandle方法的顺序是按照拦截器的配置顺序执行的,而postHandle和afterCompletion方法是逆序执行的。

4、拦截器原理

  • 在前端控制器DispatcherServlet的核心方法doDispatch上打上断点,开启debug模式后发起/hello请求来到doDispatch方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    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) {
    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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    return;
    }
    }
    //执行所有拦截器的preHandle方法,而且只要有一个拦截器返回false则直接返回,目标方法不执行。
    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);
    }
    }
    }
    }
    • ①运行getHandler(processedRequest)方法获取到该请求对应的处理器,里面包含了三个拦截器信息(两个是自定义的,一个是默认的拦截器):

    • ②通过执行mappedHandler.applyPreHandle(processedRequest, response)来执行所有拦截器的preHandle方法。且如果有一个拦截器的preHandle方法返回false(不放行)则执行afterCompletion方法后返回,也就没有了以后的流程。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
      HandlerInterceptor[] interceptors = getInterceptors();
      if (!ObjectUtils.isEmpty(interceptors)) {
      //顺序执行所有拦截器的preHandle方法。如果当前拦截器的preHandle方法返回true,则执行下个拦截器的preHandle方法;否则倒序触发已经执行过的拦截器的afterCompletion方法。而且如果有任何一个拦截器不放行则不会执行目标方法。
      for (int i = 0; i < interceptors.length; i++) {
      HandlerInterceptor interceptor = interceptors[i];
      //如果该拦截器不放行则执行该拦截器的afterCompletion方法,并返回false。
      if (!interceptor.preHandle(request, response, this.handler)) {
      triggerAfterCompletion(request, response, null);
      return false;
      }
      this.interceptorIndex = i;
      }
      }
      return true;
      }
    1
      
  • ③通过调用ha.handle(processedRequest, response, mappedHandler.getHandler())执行了目标方法。

  • ④通过调用mappedHandler.applyPostHandle(processedRequest, response, mv)逆序执行了所有拦截器的postHandle方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    //逆序执行每个拦截器的postHandle方法
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = interceptors.length - 1; i >= 0; i--) {
    HandlerInterceptor interceptor = interceptors[i];
    interceptor.postHandle(request, response, this.handler, mv);
    }
    }
    }
  • ⑤通过调用processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)进行页面渲染,且页面渲染完成后(无异常)会逆序执行拦截器的afterCompletion方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    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);
    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.isTraceEnabled()) {
    logger.trace("No view rendering, null ModelAndView returned.");
    }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    // Concurrent handling started during a forward
    return;
    }

    if (mappedHandler != null) {
    // Exception (if any) is already handled..
    //页面渲染无异常则逆序执行拦截器的afterCompletion方法。
    mappedHandler.triggerAfterCompletion(request, response, null);
    }
    }

    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
    HandlerInterceptor interceptor = interceptors[i];
    try {
    interceptor.afterCompletion(request, response, this.handler, ex);
    }
    catch (Throwable ex2) {
    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
    }
    }
    }
    }
    • ⑥如果在页面渲染中有异常,则异常被外部捕捉进入到catch语句块里的triggerAfterCompletion(processedRequest, response, mappedHandler, ex)方法来执行所有拦截器的afterCompletion方法,可见无论有没有异常,已经放行过的拦截器的afterCompletion方法都会被执行。
  • 图解如下: