前言
在spring-boot中,经常会用到滤器和拦截器,但是什么场景适合用过滤器,什么场景适合用拦截器,而且有什么异同点?下面来详细分析一下。
过滤器
过滤器Filter,是在Servlet规范中定义的,是Servlet容器支持的,该接口定义在javax.servlet
包下,主要是在客户端请求(HttpServletRequest)进行预处理,以及对服务器响应(HttpServletResponse)进行后处理。接口代码如下:
1 | package javax.servlet; |
对上面三个接口方法进行分析:
init(FilterConfig)
初始化接口,在用户自定义的Filter初始化时被调用,它与Servlet的 init方法的作用是一样的。doFilter(ServletRequest,ServletResponse,FilterChain)
在每个用户的请求进来时这个方法都会被调用,并在Servlet的service方法之前调用(如果我们是开发Servlet项目),而FilterChain就代表当前的整个请求链,通过调用FilterChain.doFilter
可以将请求继续传递下去,如果想拦截这个请求,可以不调用FilterChain.doFilter,那么这个请求就直接返回了,所以Filter是一种责任链设计模式,在spring security
就大量使用了过滤器,有一条过滤器链。destroy
当Filter对象被销毁时,这个方法被调用,注意,当Web容器调用这个方法之后,容器会再调用一次doFilter方法。
自定义Filter过滤器
在springboot自定义Filter类如下:
1 |
|
FilterRegistrationBean方式
在springboot中提供了FilterRegistrationBean方式,此类提供setOrder方法,可以为多个filter设置排序值。代码如下:
1 |
|
拦截器
拦截器是Spring提出的概念,它的作用于过滤器类似,可以拦截用户请求并进行相应的处理,它可以进行更加精细的控制。
在SpringMVC中,DispatcherServlet
捕获每个请求,在到达对应的Controller之前,请求可以被拦截器处理,在拦截器中进行前置处理后,请求最终才到达Controller。
拦截器的接口是org.springframework.web.servlet.HandlerInterceptor
接口,接口代码如下:
1 | public interface HandlerInterceptor { |
复制代码接口方法解读:
preHandle方法
对客户端发过来的请求进行前置处理,如果方法返回true,继续执行后续操作,如果返回false,执行中断请求处理,请求不会发送到ControllerpostHandler方法
在请求进行处理后执行,也就是在Controller方法调用之后处理,当然前提是之前的 preHandle方法返回 true。具体来说,postHandler方法会在DispatcherServlet进行视图返回渲染前
被调用,也就是说我们可以在这个方法中对 Controller 处理之后的ModelAndView对象进行操作afterCompletion方法
该方法在整个请求结束之后执行,当然前提依然是 preHandle方法的返回值为 true才行。该方法一般用于资源清理工作.
自定义拦截器
1 | public class MyInterceptor implements HandlerInterceptor { |
注册拦截器同时配置拦截器规则
1 |
|
多个拦截器协同工作
在springMVC中我们可以实现多个拦截器,并依次将他们注册进去,如下:
1 | public void addInterceptors(InterceptorRegistry registry) { |
拦截器的顺序也跟他们注册时的顺序有关,至少 preHandle方法是这样,下图表示了两个拦截器协同工作时的执行顺序:
后台打印日志显示了执行顺序:
1 | io-9999-exec-2] c.p.filter.interceptor.MyInterceptor : preHandle.... |
拦截器与过滤器之间的区别
从上面对拦截器与过滤器的描述来看,它俩是非常相似的,都能对客户端发来的请求进行处理,它们的区别如下:
- 作用域不同
- 过滤器依赖于servlet容器,只能在 servlet容器,web环境下使用
- 拦截器依赖于spring容器,可以在spring容器中调用,不管此时Spring处于什么环境
细粒度的不同
- 过滤器的控制比较粗,只能在请求进来时进行处理,对请求和响应进行包装
- 拦截器提供更精细的控制,可以在controller对请求处理之前或之后被调用,也可以在渲染视图呈现给用户之后调用
中断链执行的难易程度不同
- 拦截器可以 preHandle方法内返回 false 进行中断
- 过滤器就比较复杂,需要处理请求和响应对象来引发中断,需要额外的动作,比如将用户重定向到错误页面
小结
简单总结一下,拦截器相比过滤器有更细粒度的控制,依赖于Spring容器,可以在请求之前或之后启动,过滤器主要依赖于servlet,过滤器能做的,拦截器基本上都能做。