创建拦截器的方式:
方式一:在核心配置文件中配置拦截器组标签里直接创建bean对象
方式二:将拦截器注册到ioc容器中,然后再配置拦截器组标签里引用赋值
但是这两种方式都不能设置拦截的匹配路径,默认都是拦截通过了前端控制器的请求(也就是说,能拦截所有前端控制器所能处理的请求)
方式三,具体配置某个拦截器
在拦截器的匹配路径设置当中的规则:
/*只表示匹配工程目录下的请求,不是所有的请求。/**才是匹配所有的请求
拦截器中的三个抽象方法:
preHandle:在控制器方法执行前执行(它决定了是否对当前请求进行拦截,返回true是放行,返回false是拦截)
postHandle:控制器方法执行之后执行
afterHandle:模板和数据整合完成之后执行
现在我们来看前端控制器是如何使用拦截器的
在doDispatch方法中我们可以看到
其中拦截器链类的结构为
interceptorIndex应该是:当前执行到的且放行的拦截器索引
初始化拦截器链的构造方法
我们来看它是如何执行所有拦截器的三个拦截方法的
applyPreHandle方法:
遍历当前的拦截器集合,依次执行当前遍历到的拦截器的preHandle方法,如果返回false,则整个方法直接返回false,前端控制器接收到了false,直接结束doDispatch()方法以达到拦截的目的
而在返回false之前,还调用了执行所有放行的拦截器的after拦截方法
triggerAfterCompletion()用于执行所有放行的拦截器的after拦截方法
applyPostHandle方法:从拦截器集合的末尾往前进行遍历,并调用遍历到的每一个拦截的的postHandle()方法
triggerAfterCompletion方法:这个i=this.interceptorIndex就很有意思了,意思是,只遍历当前拦截器集合中没有拦截到请求的的拦截器。换个说法,就是遍历当前请求成功通过的拦截器。
还有具体的生成流程
所以,定义了多个拦截器的时候,按照上下文定义的拦截顺序生成对应的拦截器对象集合
preHandle方法是按集合从前往后执行
postHandle方法,按照集合的size()-1往前执行
afterCompletion方法:按照放行的拦截器逆序执行
因此,从源码看来,
有以上几种情况:假设有两个拦截器A,B(核心配置文件中也是这个顺序)
1、请求被所有的拦截器放行:所有的拦截器的preHandle,postHandle,afterCompletion方法都被执行
2、请求被一些拦截器放行,但是最后被拦截:这里假设请求被A放行,但是被B拦截了。这时候,只执行A的preHandle,B的preHandle,A的afterCompletion
(因为控制器方法根本就没有被执行,所以所有的postHandle不会被执行)
3、请求直接被拦截:
只执行第一个拦截器的preHandle方法
这样看来,afterCompletion方法应该是对进行视图转发前的一些处理而已。同时它如果被调用,就说明通过了当前的拦截器(但不代表请求最后没有被拦截)