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);
}
}
测试拦截器
测试结果
拦截器核心源码
首先找到DispatcherServlet.java
源码
①、②、③分别对应了拦截器的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;
}
如果为拦截器拦截了,记录当前执行拦截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);
}
}
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);
}
}
}
拦截器的执行顺序分析
-
首先执行
applyPreHandle
方法,获取拦截器数量,遍历通过 i++ 按传入的拦截器顺序调用 preHandle 拦截过滤,并记录interceptorIndex,如果被过滤则不会执行任何applyPostHandle
方法,并记录interceptorIndex调用triggerAfterCompletion
方法通过 i-- 调用已执行的拦截器的afterCompletion方法 -
上述正常情况下,执行
applyPostHandle
,获取拦截器数量,通过遍历 i-- 按传入拦截的逆序调用 postHandle 方法,并记录,并记录interceptorIndex -
上述正常情况下,执行
processDispatchResult
,之后会调用mappedHandler.triggerAfterCompletion
方法,通过interceptorIndex获取拦截器数量,遍历 i-- 按拦截器执行个数进行逆向执行afterCompletion方法 -
异常情况则直接调用triggerAfterCompletion方法,执行如上