拦截器的执行时机和原理
# 380.拦截器的执行时机和原理
我们来通过断点调试的方式,来分析原理
# 以 debug 方式启动
我们在 doDispatch 方法和 mainPage 方法上加断点,然后以 debug 方式启动,然后登录
ps:可以在 IDEA 中先取消所有断点,然后放行请求,再启用断点。先查看所有断点:
然后忽略:
或者直接忽略所有断点:
此时我们会来到 doDispatch 方法:
在执行完 getHandler 方法后,就会获取到执行链(HandlerExecutionChain),并且会包含拦截器链,可以看到有 3 个对象,其中第 0 个是我们自己写的,第 1 和 2 是自带的。
# preHandle
在调用目标方法之前,会先调用 applyPreHandle 方法,也就是调用拦截器的方法;如果拦截器里的校验不通过,就不会放行,直接 return;
如果拦截器校验通过,才会执行目标方法。
在 applyPreHandle
方法中,会拿到所有的拦截器,然后循环调用拦截器的方法:
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
注意,会先顺序执行所有拦截器的 preHandle 方法
如果返回 true,则执行下一个拦截器的 preHandle 方法。
如果返回 false(即有一个拦截器不放行),就会执行 triggerAfterCompletion
方法,该方法会逆序执行被触发过的拦截器的 afterCompletion
方法(注意 i
是递减的):
所有拦截器都放行后,才会执行目标方法
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);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
此外,在处理请求的过程中,如果有异常发生,在 catch 块中也会执行 triggerAfterCompletion
方法:
# applyPostHandle
目标方法执行完后,则会执行 applyPostHandle
方法:
该方法也很简单,就是执行所有拦截器的 postHandle 方法:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
2
3
4
5
6
7
8
9
10
# AfterCompletion
处理完请求,就是处理返回结果了,也就是执行 processDispatchResult
方法:
该方法内就会执行 triggerAfterCompletion
方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
//..... 省略其他代码
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
2
3
4
5
6
7
8
9
不难猜到,也是循环遍历执行每个拦截器的 afterCompletion
方法:
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);
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 总结
根据当前请求,找到 HandlerExecutionChain【可以处理请求的 handler 以及 handler 的所有 拦截器】
先来顺序执行 所有拦截器的 preHandle 方法
- 如果当前拦截器 prehandler 返回为 true,则执行下一个拦截器的 preHandle
- 如果当前拦截器返回为 false。直接倒序执行所有已经执行了的拦截器的 afterCompletion 方法
如果任何一个拦截器返回 false,直接 return,不执行目标方法
所有拦截器都返回 true,执行目标方法
倒序执行所有拦截器的 postHandle 方法。
前面的步骤有任何异常都会直接倒序触发 afterCompletion 方法
页面成功渲染完成以后,也会倒序触发 afterCompletion 方法
示意图: