概述
Java 里的拦截器是动态拦截 action
调用的对象,可以对 Controller 中的方法执行之前与执行之后, 及页面显示完毕后, 执行指定的方法,自定义的拦截器必须实现 HandlerInterceptor
接口
方法介绍
preHandle
????在业务处理器处理请求之前被调用
postHandle
????在业务处理器处理完请求后被调用
afterCompletion
????在 DispatcherServlet
完全处理完请求后被调用
使用 SpringMVC 拦截器
拦截所有请求
创建一个类实现 HandlerInterceptor
接口详细内容如下
/**
* @author BNTang
*/
public class MyFirstInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("处理器方法执行之前调用 → preHandle");
return false;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理器方法执行之后调用 → postHandle");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("页面显示完毕之后调用 → afterCompletion");
}
}
preHandle 返回值 true
代表放行 false
代表不放行就执行不了处理器方法
然后紧接着就是修改核心配置文件了,在配置文件当中添加 拦截器
<mvc:interceptors>
<!--设置自定义,拦截器,拦截所有请求-->
<bean class="top.it6666.web.interceptor.MyFirstInterceptor"/>
</mvc:interceptors>
启动工程,访问工程中的某一个 Controller 查看效果如下
拦截指定请求
修改 SpringMVC 核心配置文件,配置一下需要拦截的指定请求,哪些请求不需要拦截
<mvc:interceptors>
<mvc:interceptor>
<!--设置拦截哪些请求-->
<mvc:mapping path="/local"/>
<!--设置哪些请求不拦截-->
<mvc:exclude-mapping path="/exception"/>
<bean class="top.it6666.web.interceptor.MyFirstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
启动工程,访问被拦截的接口效果如下
配置多个拦截器的执行顺序
在配置了多个拦截器它们的执行顺序是怎样的呢,首先先把 preHandle
方法的返回值都改为 tru
也就是放行,然后启动工程访问 local
接口,查看控制台的输出信息如下
结果发现,preHandle
是按顺序执行了,而其他的都是按照倒序执行的,至于为什么就要来看看源码了,源码分析如下, 多个拦截器都返回 true
的源码执行流程如下,首先来看一下 preHandle
、postHandle
、afterCompletion
这三个方法的分别调用时机吧,还是一样的从 DispatcherServlet
开始,调用时机如下图
调用时机看完了之后,现在就一个一个来分析它内部的执行流程吧,先来看 applyPreHandle
先不看别的就看循环它是怎么循环的即可,该方法的循环为 ++
,那么就说明了是按照顺序一个一个取出进行执行了
经过如上的图就可以得出 preHandle
为啥是按顺序执行的了,那么紧接着就来看 postHandle
为啥是倒序执行吧,postHandle 对应的源码方法为 applyPostHandle
进入代码内容发现该方法的循环为 –
那么这也就是为啥是倒序执行的原因了
废话不多说,最后一个直接上源码了,原理同上
如上都是返回 true
的情况,那么返回 false
的情况呢,如下,第一个拦截器返回 false
第二个拦截器返回 true
的执行流程如下
首先调用 applyPreHandle
的时候里面有四个拦截器其中有一个是 SpringMVC 弄的,另外的3个是我们自己弄的,当循环走到我们的倒数第二个拦截器的时候你会发现,它进入到了 triggerAfterCompletion
方法内部,那么为啥呢,就因为你在 preHandle
方法中返回了一个 false 然后非一下就成了 true 然后就条件成立进入了
那么 triggerAfterCompletion
方法内部主要做了什么呢,在进入 triggerAfterCompletion
方法之前 interceptorIndex
的索引角标为 1
而 i 却是等于了 2
进入 triggerAfterCompletion
方法内部,查看详细内容如下,一样的是取出所有的拦截器数组
不过这个循环你需要注意一下了,它进入这个方法的时候 interceptorIndex
是为 1
那么我们的拦截器已经不会再从拦截器数组中获取了这也就是为什么只是输出了一个 preHandle
的原因了
这个方法执行完毕之后在往回执行直接返回了一个 false 在往上又进行了非的逻辑运算符又成了 true 然后就直接结束了后面的都不会再执行了
如上是第一个拦截器返回 false 第二个拦截器返回 true 的情况的执行流程,那么第一个拦截器返回 true 第二个拦截器返回 false 的执行流程是怎样的呢,自行修改 true false, 我这里直接讲解源码流程
如上图的解释就是我们自定义的拦截器第一个返回的是 true 那么 preHandle
执行了,preHandle 返回的是 true 这个时候不会进入 triggerAfterCompletion
然后继续往下取,取角标为 3
的拦截器这个时候 preHandle
执行了打印了我们的内容,而这个时候 preHandle
返回的是 false 非一下成为了 true
会进入 triggerAfterCompletion
方法体中,这个时候你需要注意一个变量那么就是 interceptorIndex
这个值,在最后一个拦截器进入到 triggerAfterCompletion
方法体的时候 interceptorIndex
这个值为 2
为 2
,那么这个时候又去拦截器数组倒序取,那么最后一个拦截器是不会取出的,只取了第一个,那么第一个的 afterCompletion
执行了也就是会打印我们在 afterCompletion
方法体中编写的内容
执行完毕返回 false 最外面的方法运用了非的逻辑运算符就成了 true 条件成立导致后面的就都不会在执行了
内部源码分析
还是一样的套路先从 DispatcherServlet
进入,查看 doDispatch
如上方框框出来的内容是代表,要应用拦截器,点击方法进入进去查看,进入之后的代码如下
如上的内容你就关注两个点,我方框框着的点即可,如果返回 false
到了之前的那个方法,非一下就成了 true
下面的内容就不在执行了
而返回 true
就不一样了,返回 true
下面的内容就还会继续执行