过滤器 (Filter)
过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理 通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理
过滤器依赖于servlet容器,实现基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换等等操作,便于代码重用,不必每个servlet中进行冗余操作
使用Filter完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行预处理并生成响应,最后Filter再对服务器响应进行后处理
Filter有如下几个用处
-
在HttpServletRequest到达Servlet之前,拦截客户的HttpServletRequest
-
根据需要检查HttpServletRequest,也可以修改HttpServletRequest头和数据
-
在HttpServletResponse到达客户端之前,拦截HttpServletResponse
-
根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据
代码
1.创建一个Fileter只需要两步
1)创建一个类,实现Filter接口
2)配置Filter
- 也可以通过xml文件进行配置,小编这里不做介绍! 大家可以看到只需要两步就能完成一个简单的过滤器,下面我们进行简单的测试
2.测试
1)创建一个类
@RestController
public class VaeBookController {
@Autowired
private VaeBookService vaeBookService;
@GetMapping("/get")
public List<VaeBookPO> get() {
return vaeBookService.getVaeBook();
}
}
2)发起请求
此时可以看到过滤器已经生效,由于它是在请求到达Servlet之前,拦截客户的请求,所以我们可以通过它来完成一些对请求的过滤,校验等操作
扩展:多个Filter的执行顺序
1)再定义一个过滤器FilterTest2
2)对新定义的过滤器FilterTest2进行配置
3)再次发起请求
我们可以看到两个过滤器都生效了,那么他们的顺序是由什么控制的呢?
registration.setFilter(new FilterTest()); //添加过滤器
registration.addUrlPatterns("/*"); //设置过滤路径,/*代表所有路径
registration.setName("FilterTest"); //设置过滤器名称
registration.setOrder(1); //设置优先级,数字越小优先级越高,越先执行
还有其他的配置项,感兴趣的小伙伴可以自己去测试一下,小编这里不一一介绍了
总结
-
Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁
-
启动服务器时加载过滤器的实例,并调用init()方法来初始化实例
-
每一次请求时都只调用方法doFilter()进行处理
-
停止服务器时调用destroy()方法,销毁实例
-
过滤器处理链过程:浏览器发出的请求先递交给第一个filter进行过滤,符合规则则放行,递交给filter链中的下一个过滤器进行过滤。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉
拦截器 (Interceptor)
拦截器,在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。拦截是AOP的一种实现策略
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用
拦截器将很多功能从我们的Action中独立出来,大量减少了我们Action的代码,独立出来的行为就有很好的重用性
拦截器是对调用的Controller起作用,它提供了一种机制可以使开发者可以定义在一个请求执行的前后执行的代码,也可以在一个请求执行前阻止其执行
代码
1.创建一个拦截器
1)创建一个类,实现HandlerInterceptor接口
2)配置拦截器
2.测试
我们用上边创建好的类,发起请求
此时可以看到拦截器已经生效,当你提交对Action(默认是.action结尾的url)的请求时,ServletDispatcher会根据你的请求,去调度并执行相应的Action。在Action执行之前,调用被Interceptor截取,Interceptor在Action执行前后执行。据此,我们可以针对一些请求作出拦截,完成一些业务需求
扩展:多个Interceptor的执行顺序
1)再定义一个过滤器InterceptorTest2
2)对新定义的过滤器InterceptorTest2进行配置
3)再次发起请求
我们可以看到两个过滤器都生效了,那么他们的顺序是由什么控制的呢?
registration.addPathPatterns("/**"); //配置拦截的路径
registration.order(0); //配置优先级,数字越小优先级越高,越先触发
//添加不拦截路径
registration.excludePathPatterns(
//html静态资源
"/**/*.html",
//js静态资源
"/**/*.js",
//css静态资源
"/**/*.css",
"/**/*.woff",
"/**/*.ttf"
);
注意:
如果在添加不拦截路径当中,配置了方法路径,那么方法将不会被拦截器拦截
如图,此时调用login方法,配置了两个拦截器,只有一个生效
总结
-
拦截器1 preHandle不放行,postHandle和afterCompletion不会执行
-
拦截器1 preHandle不放行,拦截器2不执行
-
只要有一个拦截器不放行,postHandle不会执行
-
由于拦截器是基于web框架的调用,拦截器可以调用IOC容器中的各种依赖,而过滤器不能,因此可以使用Spring的依赖注入进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理
过滤器与拦截器使用场景
1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录)
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现
过滤器与拦截器区别
- 实现原理不同
过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的
- 使用范围不同
我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用
而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中
- 触发时机不同 过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束
-
拦截的请求范围不同 过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用
-
注入Bean情况不同 我们分别在过滤器和拦截器中都注入service,过滤器中注入service,发起请求方法正常,而拦截器中注入service,发起请求方法报错,这是因为加载顺序导致的问题,拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理
解决方案也很简单,我们在注册拦截器之前,先将Interceptor 手动进行注入。注意:在registry.addInterceptor()注册的是getMyInterceptor() 实例
- 控制执行顺序不同 过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行
拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行
先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行
两者的本质区别:从灵活性上说拦截器功能更强大些,Filter能做的事情,他都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的