过滤器和拦截器的区别(拦截器和过滤器的简单解释)
周末有朋友加我微信,问我一个问题:大哥,()和()有什么区别?听到标题,我的第一感觉就是:“简单”!
毕竟这两个工具在开发中的使用频率都挺高的,应用也比较简单,但是刚要回复他的时候,却不知从何下手。不,我已经工作了这么长时间来回答这样一个基本问题,我失去了我的主人。
我通常会想到简单的知识点,但通常不会太注意细节。曾经被别人问过,我也说不出原因。
说到底还是对这些知识了解不够,一直停留在会用的阶段,以至于现在“见了就没用”!这是底子薄的典型表现,哎~,其实我就是浮肿了!
知耻而后勇。下面我们结合实践,更直观地感受一下两者的区别。
过滤器的配置相对简单。可以直接实现接口,也可以通过@注解拦截具体的url。可以看到接口中定义了三个方法。
init() :该方法在容器开始初始化过滤器时调用,整个生命周期只会调用一次。【注意】:该方法必须执行成功,否则过滤器将不起作用。
() : 容器中的每一个请求都会调用这个方法来调用下一个过滤器。
():当容器销毁过滤器实例时调用该方法。一般在方法中销毁或关闭资源,在过滤器的整个生命周期中只会调用一次。
@
班级 {
@
无效初始化(){
.out.("前置");
}
@
空白 ( , , ) , {
.out.("处理中");
.(, );
}
@
空白 () {
。前哨”);
}
}
拦截器是一个链式调用。一个应用中可以同时存在多个拦截器,一个请求也可以触发多个拦截器,每个拦截器的调用会按照其声明的顺序依次执行。
先写一个简单的拦截器处理类,通过传递实现对请求的拦截,看到接口中也定义了三个方法。
() : 这个方法会在请求被处理之前被调用。“注意”:如果该方法的返回值为false,则视为本次请求结束,不仅自身拦截器失效,还会导致其他拦截器不再执行。
():只有当()方法的返回值为true时才会执行。将在调用 in 中的方法之后,在返回渲染视图之前调用。《有趣的事》:()方法的调用顺序与()相反。最先声明的拦截器()方法先执行,而()方法后执行。
():只有当()方法的返回值为true时才会执行。整个请求结束,对应的视图渲染完成后执行。
@
班级 {
@
( , , ) {
.out.("前置");
真的;
}
@
空白 ( , , , ) {
.out.("处理中");
}
@
无效(,,,前){
。前哨”);
}
}
注册自定义拦截器处理类,通过 .
@
班级 {
@
空白 ( ) {
。(新的 ())。(”/**”);
。(新的 ())。(”/**”);
}
}
过滤器和拦截器都体现了AOP编程思想,都可以实现日志记录和登录认证等功能,但是两者之间有很多区别,接下来会一一说明。
过滤器和拦截器的底层实现是非常不同的。过滤器是基于函数回调实现的,而拦截器是基于Java的反射机制(动态代理)实现的。
专注于这里的过滤器!
我们自定义的过滤器中会执行一个()方法,这个方法有一个参数,但实际上是一个回调接口。in是它的实现类,这个实现类内部还有一个()方法,就是回调方法。
{
void ( 变量 1, 变量 2) , ;
}
中,我们可以获取到我们的自定义类,在其内部回调方法中调用每个自定义过滤器(),并执行()方法。
{ 的最后一堂课
@
空白 ( , ) {
。忽略
(,);
}
空白 ( , ) {
如果(位置 < n){
//获取位置
图 = [位置];
=.();
...
。(, , 这个);
}
}
}
并且各自会先执行自己的()过滤逻辑,最后在执行结束前执行.(,),即回调in的()方法,通过循环执行实现函数回调。
@
空白 ( , , ) , {
.(, );
}
我们看到实现了javax..接口,而这个接口是在规范中定义的,也就是说的使用依赖于容器,所以只能在web程序中使用。
拦截器()是一个组件,由容器管理。它不依赖于其他容器,可以单独使用。不仅可以用在web程序中,还可以用在 , Swing 等程序中。
过滤器和拦截器的触发时机也不一样。让我们看看下面的图片。
过滤器在请求进入容器之后,但在进入之前进行预处理,请求结束是在处理之后。
拦截器在请求进入后和进入前进行预处理,在相应的视图渲染完成后请求结束。
上面我们同时配置了过滤器和拦截器,然后构建接收请求进行测试。
@
@()
类测试{
@("/测试1")
@
测试 1(a) {
.out.("我是");
无效的;
}
}
在项目启动过程中,发现的init()方法是随着容器的启动而初始化的。
这时候浏览器发送请求,F12看到其实有两个请求,一个是我们自定义的请求,一个是访问静态图标资源的请求。
看到控制台的打印日志如下:
执行顺序:处理->预处理->我->处理->处理后
加工
正面
加工
后部
加工
过滤器执行两次,拦截器只执行一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对中间请求或访问目录下的资源请求起作用。
在实际业务场景中,应用于过滤器或拦截器时,不可避免地会引入一些服务来处理业务逻辑。
接下来我们在过滤器和拦截器中都注入,看看有什么区别?
@
班级 {
@
无效一个(){
.out.("我是方法A");
}
}
注入过滤器,发起请求测试,日志正常打印“我是方法A”。
@
;
@
空白 ( , , ) , {
.out.("处理中");
。一个();
.(, );
}
加工
我是方法A
正面
我是
加工
后部
在拦截器中注入,发起请求测试一下,结果TM报错,调试发现为什么注入为Null?
这是加载顺序引起的问题。之前加载拦截器,Bean由它来管理。
拦截者:我今天要进洞房;哥:你别闹,我还没生你媳妇呢!
解决方法也很简单,我们在注册拦截器之前手动注入。“注意”:() 实例注册在.() 中。
@
班级 {
@豆
(){
.out.("注入");
新的 ();
}
@
空白 ( ) {
.(()).("/**");
}
}
在实际开发过程中,会同时存在多个过滤器或拦截器。但是,有时我们希望某个过滤器或拦截器先执行,这就涉及到它们的执行顺序。
过滤器使用@Order注解来控制执行顺序,过滤器的级别由@Order控制。值越小,级别越高,最先执行。
@命令(。)
@
班级 {
拦截器默认的执行顺序是它的注册顺序,也可以通过Order手动设置和控制。值越小,执行越早。
@
空白 ( ) {
.(new ()).("/**").order(2);
.(new ()).("/**").order(1);
.(new ()).("/**").order(3);
}
看输出结果,发现先声明的拦截器()方法先执行,但是()方法后执行。
()方法的调用顺序其实和()是相反的!如果在实际开发中对执行顺序有严格要求,这一点要特别注意。
正面
正面
正面
我是
加工
加工
加工
后部
治疗后
治疗后
“那为什么会这样?” 要得到答案,只能看源码了。我们要知道,服务器中的所有请求都必须经过核心组件进行路由,其()方法才会被执行,拦截器()和()方法在其中被调用。
空白 ( , ) {
尝试 {
…………
尝试 {
// 获取当前可以执行的适配器
ha = (.());
// last- ,如果由 .
=.();
isGet = "GET".();
如果 (isGet || "HEAD".()) {
long = ha.(, .());
如果 (。()) {
.debug("[" () "] 的最后一个值是:" );
}
如果 (新的 (, ).() && isGet) {
;
}
}
// 注意:执行()方法
如果 (!。(, )) {
;
}
// 注意:【包括我们的业务逻辑,当抛出异常时,会被Try并捕获】
mv = ha.(, , .());
如果 (。()) {
;
}
(, MV);
// 注意:执行中的方法【抛出异常时不能执行】
.(, , MV);
}
}
…………
}
看看(),()这两个方法是怎么调用的,就会明白为什么()和()的执行顺序是相反的。
( , ) {
[] = 这个.();
如果(!。()) {
for(int i = 0; i < .; this. = i ) {
= [我];
如果这。)) {
this.on(, , ()null);
错误的;
}
}
}
真的;
}
无效(,,@mv){
[] = 这个.();
如果(!。()) {
for(int i = . - 1; i >= 0; --i) {
= [我];
.(, , 这个., mv);
}
}
}
事实证明,当两个方法调用拦截器 array[] 时,循环的顺序是相反的。. . ,导致 () 和 () 方法的执行顺序颠倒。
相信大多数人都能熟练使用过滤器和拦截器,但是两者的区别还是需要多了解一下,不然在开发中使用不当时不时会出现奇怪的问题。以上内容比较简单,新手要向老手学习复习,希望大家积极补充缺失的部分。如有理解上的错误,还望大家不吝赐教。