SpringMVC HandlerInterceptor拦截器源码解析
提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、实例
写一个类继承HandlerInterceptor接口,重写其中的方法
1.单个拦截器
public class MyFirstInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle.....");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle......");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion......");
}
}
注册
<mvc:interceptors>
<bean class="edu.qust.controller.MyFirstInterceptor"></bean>
</mvc:interceptors>
Controller
@Controller
public class InterceptorTestController {
@RequestMapping("/test01")
public String test01(){
System.out.println("test01方法调用");
return "success";
}
}
执行顺序 
单个拦截器的执行顺序
return true并且无异常
preHandle ? 调用方法 ? postHandle ? 到达页面 ? afterCompletion
return false 后续内容将会被拦截,不再执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle.....");
return false;
}

return true 有异常 仍会执行afterCompletion,但不执行postHandle()


2.多个拦截器
执行顺序取决于配置顺序 配置顺序,这里是先配置的2号拦截器,即preHandle…222
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test01"/>
<bean class="edu.qust.controller.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<mvc:interceptors>
<bean class="edu.qust.controller.MyFirstInterceptor"></bean>
</mvc:interceptors>
新增的secondInterceptor
public class MySecondInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle.....222");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle......222");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion......222");
}
}
两个preHandle都return true的情况 虽然名字是2号,但因为配置顺序是先2后1,所以会先执行2 
多个拦截器执行顺序
(先配置的)preHandle ? (后配置的)preHandle ? 调用方法 ? (后配置的)postHandle ? (先配置的)postHandle ? 到达页面 ? (后配置的)afterCompletion ? (先配置的)afterCompletion
preHandle()==>谁配置在前谁先,post/after ? 的谁配置在后谁先
上面是两个拦截器的handle都return true的情况,现在设置return false的情况
让2号return true ,1号 return false的情况  发现2号的afterCompletion执行了 ==>放行preHandle的会执行自己的afterCompletion 所以没有放行的1号并没有执行自己的afterCompletion
那么为什么会这样执行,放行了preHandle的拦截器为什么会执行自身的afterCompletion
二、源码 DispatcherServlet.class中寻找原因
1.找到preHandle()执行的位置
找到在DispatcherServlet.class实际干活的doDispatch(),开始debug  在执行getHandler中发现,拿到的不只是可以用来处理的类handler,还有拦截器集合interceptorList  发现其中有3个拦截器 还有一个默认为-1的拦截器索引  1和2是我们自己设计的拦截器
接着向下执行
执行到了下面这句时,控制台打印了   看英文能看出来 applyPreHandle 就是 执行preHandle嘛
此时我们找到了拦截器执行preHandle()的位置
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}
这下得进去看看为什么放行了preHandle就会执行他的afterCompletion
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = this.getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
this.triggerAfterCompletion(request, response, (Exception)null);
return false;
}
}
}
return true;
}
这时候之前拿到的拦截器集合就出现了,可以发现是做遍历拦截器的操作,如果当前拦截器的preHandle返回的是false则会执行if语句里的内容 ===> 调用triggerAfterCompletion,然后return false直接结束
这里要注意 :源码中的 triggerAfterCompletion ≠ afterCompletion
这里很容易误会成返回false才执行afterCompletion,这不就成了返回false才执行afterCompletion了吗? 然而他们俩并不是一个方法,上面的例子很明显说明了
preHandle返回了true才执行自身的afterCompletion
看for循环中,出现了之前的拦截器索引 interceptorIndex ,之前这个数值为 -1 ,现在做自增循环,很明显,这是在记录个数,以本身为下标,一直+1 ,直到出现了第一个return false的拦截器出现。
遍历的顺序就是配置的顺序  在执行到第3个(下标为2)的MyFirstInterceptor时,因为设置了return false,所以执行了if中的语句  进入this.triggerAfterCompletion(request, response, (Exception)null);
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception {
HandlerInterceptor[] interceptors = this.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 var8) {
logger.error("HandlerInterceptor.afterCompletion threw exception", var8);
}
}
}
}
发现还是在遍历之前的拦截器集合,但这次看到了真正的afterCompletion
看这次的for循环,i是基于之前interceptorIndex做自减循环,而interceptorIndex则是用来记录有多少return true的拦截器,本身就是数组的下标。
这下就清楚了,这是把interceptorIndex作为下标,把集合中interceptorIndex之前的拦截器的afterCompletion都执行一遍。所以就实现了return true的会执行afterCompletion的效果
所以return true的2号会打印afterCompletion,而return false的1号则没有执行
现在把两个都改成return true再debug,不然下面会被拦截不执行
找完问题的原因后继续向下走,接着执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 (mv的源码分析补在以后)
2.找到preHandle()执行的位置
继续执行   找到了执行postHandle的位置
mappedHandler.applyPostHandle(processedRequest, response, mv);
进去看看如何执行postHandle()
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
HandlerInterceptor[] interceptors = this.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);
}
}
}
发现依旧是遍历,但是这次是倒叙遍历,这也就说明了为什么先配置的拦截器会后执行postHandle()
回到doDispatch(),继续走,进入有渲染页面方法的processDispatchResult()中
3.afterCompletion相关
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) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
执行到 进行页面渲染,控制台打印  若this.render正常执行,接着则执行triggerAfterCompletion去执行afterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);}
若this.render有问题,下面的catch也会捕获,接着执行triggerAfterCompletion,这就是为什么i=1/0这种异常程序依旧会执行afterCompletion的原因

|