Spring MVC中,拦截器的执行顺序可以通过两种方式进行指定:

  • 使用@Order注解,使用@Order注解可以指定拦截器的执行顺序。@Order注解的value值表示拦截器执行的顺序,数值越小的拦截器优先执行。
  • 使用order()方法,在addInterceptor()方法中,可以使用order()方法指定拦截器的执行顺序。order()方法的参数是一个整数值,数值越小的拦截器优先执行
  • 如果使用了多个拦截器,那么它们的执行顺序将按照添加到拦截器链中的顺序执行。也就是说,先添加的拦截器先执行,后添加的拦截器后执行。如果使用了@Order注解或order()方法来指定拦截器的执行顺序,则会按照指定的顺序执行。

创建拦截器

创建一个实现了 HandlerInterceptor 接口的拦截器类。该接口定义了 3 个方法,分别在请求处理之前、请求处理之后以及完成请求处理后调用。

	package com.xy.intercept.intercept;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 谢阳
 * @version 1.8.0_131
 * @date 2023/3/16 8:19
 * @description
 */
@Component
public class MyIntercept1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // preHandle方法在请求处理前调用,返回true表示继续处理请求,返回false则中断请求处理
        System.out.println("preHandle1...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // postHandle方法在请求处理后调用,但在视图渲染前,可以在这里对模型和视图进行进一步的处理
        System.out.println("postHandle1...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // afterCompletion方法在视图渲染后调用,可以用于释放资源等操作
        System.out.println("afterCompletion1...");
    }

}
package com.xy.intercept.intercept;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author 谢阳
 * @version 1.8.0_131
 * @date 2023/3/16 8:19
 * @description
 */
@Component
public class MyIntercept2 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // preHandle方法在请求处理前调用,返回true表示继续处理请求,返回false则中断请求处理
        System.out.println("preHandle2...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        // postHandle方法在请求处理后调用,但在视图渲染前,可以在这里对模型和视图进行进一步的处理
        System.out.println("postHandle2...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // afterCompletion方法在视图渲染后调用,可以用于释放资源等操作
        System.out.println("afterCompletion2...");
    }

}

注册拦截器

在Java配置中注册拦截器。如果使用Java配置来配置Spring MVC,可以通过实现WebMvcConfigurer接口中的addInterceptors()方法注册拦截器。

package com.xy.intercept.config;

import com.xy.intercept.intercept.MyIntercept1;
import com.xy.intercept.intercept.MyIntercept2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 谢阳
 * @version 1.8.0_131
 * @date 2023/3/16 9:29
 * @description
 */
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private MyIntercept1 myIntercept1;

    @Autowired
    private MyIntercept2 myIntercept2;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myIntercept2).addPathPatterns("/**").order(2);
        registry.addInterceptor(myIntercept1).addPathPatterns("/**").order(1);
    }
}

测试拦截器

image.png

测试结果

image.png

拦截器核心源码

首先找到DispatcherServlet.java源码

image.png

①、②、③分别对应了拦截器的preHandle、postHandle、afterCompletion方法,④对应异常情况下的afterCompletion方法

applyPreHandle源码

/**
 * Apply preHandle methods of registered interceptors.
 * @return {@code true} if the execution chain should proceed with the
 * next interceptor or the handler itself. Else, DispatcherServlet assumes
 * that this interceptor has already dealt with the response itself.
 */
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	for (int i = 0; i < this.interceptorList.size(); i++) {
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		if (!interceptor.preHandle(request, response, this.handler)) {
			triggerAfterCompletion(request, response, null);
			return false;
		}
		this.interceptorIndex = i;
	}
	return true;
}

image.png

如果为拦截器拦截了,记录当前执行拦截list中多对应的地址,调用triggerAfterCompletion方法如下triggerAfterCompletion源码所示

applyPostHandle源码

/**
 * Apply postHandle methods of registered interceptors.
 */
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
      throws Exception {

   for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
      HandlerInterceptor interceptor = this.interceptorList.get(i);
      interceptor.postHandle(request, response, this.handler, mv);
   }
}

image.png processDispatchResult源码

/**
 * Handle the result of handler selection and handler invocation, which is
 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
 */
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..
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

正常情况下调用倒数第三行代码

triggerAfterCompletion源码

/**
 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
 * has successfully completed and returned true.
 */
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);
      }
   }
}

image.png

拦截器的执行顺序分析

  • 首先执行applyPreHandle方法,获取拦截器数量,遍历通过 i++ 按传入的拦截器顺序调用 preHandle 拦截过滤,并记录interceptorIndex,如果被过滤则不会执行任何applyPostHandle方法,并记录interceptorIndex调用triggerAfterCompletion方法通过 i-- 调用已执行的拦截器的afterCompletion方法

  • 上述正常情况下,执行applyPostHandle,获取拦截器数量,通过遍历 i-- 按传入拦截的逆序调用 postHandle 方法,并记录,并记录interceptorIndex

  • 上述正常情况下,执行processDispatchResult,之后会调用mappedHandler.triggerAfterCompletion方法,通过interceptorIndex获取拦截器数量,遍历 i-- 按拦截器执行个数进行逆向执行afterCompletion方法

  • 异常情况则直接调用triggerAfterCompletion方法,执行如上