过滤器和拦截器
我们在项目中同时配置
拦截器
和 过滤器
。1、过滤器 (Filter)
过滤器的配置比较简单,直接实现
Filter
接口即可,也可以通过@WebFilter
注解实现对特定URL
拦截,看到Filter
接口中定义了三个方法。init()
:该方法在容器启动初始化过滤器时被调用,它在Filter
的整个生命周期只会被调用一次。「注意」:这个方法必须执行成功,否则过滤器会不起作用。
doFilter()
:容器中的每一次请求都会调用该方法,FilterChain
用来调用下一个过滤器Filter
。
destroy()
:当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器Filter
的整个生命周期也只会被调用一次
2、拦截器 (Interceptor)
拦截器它是链式调用,一个应用中可以同时存在多个拦截器
Interceptor
, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。首先编写一个简单的拦截器处理类,请求的拦截是通过
HandlerInterceptor
来实现,看到HandlerInterceptor
接口中也定义了三个方法。preHandle()
:这个方法将在请求处理之前进行调用。「注意」:如果该方法的返回值为false
,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
postHandle()
:只有在preHandle()
方法返回值为true
时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。「有意思的是」:postHandle()
方法被调用的顺序跟preHandle()
是相反的,先声明的拦截器preHandle()
方法先执行,而postHandle()
方法反而会后执行。
afterCompletion()
:只有在preHandle()
方法返回值为true
时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。
将自定义好的拦截器处理类进行注册,并通过
addPathPatterns
、excludePathPatterns
等属性设置需要拦截或需要排除的 URL
。我们不一样
过滤器 和 拦截器 均体现了
AOP
的编程思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。1、实现原理不同
过滤器和拦截器 底层实现方式大不相同,
过滤器
是基于函数回调的,拦截器
则是基于Java的反射机制(动态代理)实现的。这里重点说下过滤器!
在我们自定义的过滤器中都会实现一个
doFilter()
方法,这个方法有一个FilterChain
参数,而实际上它是一个回调接口。ApplicationFilterChain
是它的实现类, 这个实现类内部也有一个 doFilter()
方法就是回调方法。里面能拿到我们自定义的
类,在其内部回调方法
里调用各个自定义
过滤器,并执行
方法。
而每个
xxxFilter
会先执行自身的 doFilter()
过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse)
,也就是回调ApplicationFilterChain
的doFilter()
方法,以此循环执行实现函数回调。2、使用范围不同
我们看到过滤器 实现的是
接口,而这个接口是在
规范中定义的,也就是说过滤器
的使用要依赖于
等容器,导致它只能在
程序中使用。
而拦截器(
) 它是一个
组件,并由
容器管理,并不依赖
等容器,是可以单独使用的。不仅能应用在
程序中,也可以用于
、
等程序中。
3、触发时机不同
和
的触发时机也不同,我们看下边这张图。
过滤器
Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。拦截器
Interceptor
是在请求进入servlet
后,在进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。4、拦截的请求范围不同
在上边我们已经同时配置了过滤器和拦截器,再建一个
Controller
接收请求测试一下。项目启动过程中发现,过滤器的
方法,随着容器的启动进行了初始化。
此时浏览器发送请求,F12 看到居然有两个请求,一个是我们自定义的
请求,另一个是访问静态图标资源的请求。
看到控制台的打印日志如下:
执行顺序 :
Filter 处理中
-> Interceptor 前置
-> 我是controller
-> Interceptor 处理中
-> Interceptor 处理后
过滤器
Filter
执行了两次,拦截器Interceptor
只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller
中请求或访问static
目录下的资源请求起作用。5、注入Bean情况不同
在实际的业务场景中,应用到过滤器或拦截器,为处理业务逻辑难免会引入一些
service
服务。下边我们分别在过滤器和拦截器中都注入
service
,看看有什么不同?过滤器中注入
service
,发起请求测试一下 ,日志正常打印出“我是方法A”
。在拦截器中注入
,发起请求测试一下 ,竟然TM的报错了,
跟一下发现注入的
怎么是
啊?
这是因为加载顺序导致的问题,
加载的时间点在
之前,而
又是由
进行管理。
❝拦截器:老子今天要进洞房;Spring:兄弟别闹,你媳妇我还没生出来呢!
解决方案也很简单,我们在注册拦截器之前,先将
Interceptor
手动进行注入。「注意」:在registry.addInterceptor()
注册的是getMyInterceptor()
实例。6、控制执行顺序不同
实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。
过滤器用
@Order
注解控制执行顺序,通过@Order
控制过滤器的级别,值越小级别越高越先执行。拦截器默认的执行顺序,就是它的注册顺序,也可以通过
Order
手动设置控制,值越小越先执行。看到输出结果发现,先声明的拦截器
preHandle()
方法先执行,而postHandle()
方法反而会后执行。postHandle()
方法被调用的顺序跟 preHandle()
居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。「那为什么会这样呢?」 得到答案就只能看源码了,我们要知道
controller
中所有的请求都要经过核心组件DispatcherServlet
路由,都会执行它的 doDispatch()
方法,而拦截器postHandle()
、preHandle()
方法便是在其中调用的。看看两个方法
applyPreHandle()
、applyPostHandle()
具体是如何被调用的,就明白为什么postHandle()
、preHandle()
执行顺序是相反的了。发现两个方法中在调用拦截器数组
时,循环的顺序竟然是相反的。。。,导致
、
方法执行的顺序相反。
总结
我相信大部分人都能熟练使用滤器和拦截器,但两者的差别还是需要多了解下,不然开发中使用不当,时不时就会出现奇奇怪怪的问题,以上内容比较简单,新手学习老鸟复习,有遗漏的地方还望大家积极补充,如有理解错误之处,还望不吝赐教。
原创不易,「燃烧秀发输出内容」,如果你有一丢丢收获,点个 「在看」 或者 「转发」 鼓励一下哦!
Loading...