Skip to content

过滤器和拦截器的区别

拦截器 过滤器

Filter 的使用

首先,要使用 Filter,必须实现 javax.servlet.Filter 接口:

java
public interface Filter {
    //web应用加载进容器,Filter对象创建之后,执行init方法初始化,用于加载资源,只执行一次。 
    public default void init(FilterConfig filterConfig) throws ServletException {}
    //每次请求或响应被拦截时执行,可执行多次。
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException;
    //web应用移除容器,服务器被正常关闭,则执行destroy方法,用于释放资源,只执行一次。
    public default void destroy() {}
}
  • init 和 destroy 是 default 方法,实现类可以不用实现。
  • doFilter 必须实现,也就是说,作为一个过滤器,doFilter 必须要定义。
  • doFlilter 方法中传进来的 FilterChain 对象用来调用下一个过滤器。

拦截器的使用

java
public interface HandlerInterceptor {
    //拦截handler的执行 --> 在HanlerMapping决定适合的handler之后,[在HandlerAdater调用handler之前执行。]
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
        return true;
    }
    //拦截handler的执行 --> [在HandlerAdapter调用handler之后],在DispatcherServlet渲染视图之前执行
    default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                            @Nullable ModelAndView modelAndView) throws Exception {
    }
    //视图渲染后调用,且只有preHandle结果为true,才会调用
    default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                 @Nullable Exception ex) throws Exception {
    }
}
java
//DispatcherServlet
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return; //遍历所有的interceptors,调用preHandle方法,只有返回true,才能进行下去
}
// 这里也就是处理Contrller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//视图渲染
applyDefaultViewName(processedRequest, mv);
//视图渲染之后调用
mappedHandler.applyPostHandle(processedRequest, response, mv);

过滤器与拦截器的区别

一、实现原理不同

  • 过滤器的实现基于回调函数
  • 拦截器基于 Java 的反射机制【动态代理】实现。

二、使用范围不同

  • 过滤器是 Servlet 的规范,需要实现 javax.servlet.Filter 接口,Filter 使用需要依赖于 Tomcat 等容器。
  • 拦截器是 Spring 组件,定义在 org.springframework.web.servlet 包下,由 Spring 容器管理【有更加丰富的生命周期处理方法,细粒度,且能够使用 Spring 中的资源】,不依赖 Tomcat 等容器。

三、触发时机不同

这一段在 HandlerInterceptor 类的注释上可以发现,两者的触发时机是不同的:

1600131680713-1f602396-44ae-4a01-9a81-fbc0cc3703d2.png

  • 过滤器:对请求在进入后 Servlet 之前或之后进行处理。
  • 拦截器:对请求在 handler【Controller】前后进行处理。

1600131680802-431bc7b7-9c28-4dae-94f3-984f27b36667.png

四、执行顺序不同

同时配置了过滤器和拦截器的情形:

bash
MyFilter1
MyFilter2
MyInterceptor1 在Controller前执行
MyInterceptor2 在Controller前执行
controller方法执行...
MyInterceptor2 Controller之后,视图渲染之前
MyInterceptor1 Controller之后,视图渲染之前
MyInterceptor2 视图渲染完成之后执行
MyInterceptor1 视图渲染完成之后执行
MyFilter2
MyFilter1
  • 过滤器的顺序

每一次都将 chain 对象传入,达到最后接口回调的效果:

1600131680803-3abdc535-cab6-4d3f-a5d3-4a84c6078097.png

  • 拦截器的顺序

preHandle1 -> preHande2 -> 【Controller】 -> postHandle2 -> postHandle1 -> afterCompletion2 -> afterComplention1 preHandle 按照注册顺序,后两个与注册顺序相反。

  • 一个拦截器的 preHandle 为 false,则之后的所有拦截器都不会执行。
  • 一个拦截器的 preHandle为 true,则这个拦截器的triggerAfterCompletion 一定会执行。
  • 只有所有的拦截器 preHandler都为 true,也就是正常执行,postHandle 才会执行。
java
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            //一旦当前拦截器preHandle的返回值为false,那么从上一个可用的拦截器的afterCompletion开始
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false; //这里返回false意为 后续不进行下去了。
            }
            this.interceptorIndex = i;//interceptorIndex初始化为-1,只有当前拦截器preHandle为true,才会赋值当前的i。
        }
    }
    return true;
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
    throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    for (int i = this.interceptorIndex; i >= 0; i--) 
    }

五、控制执行顺序方式不同

两者默认都是使用注册顺序,如果想要认为控制执行的顺序,方式略有不同:

  • 过滤器
    • 如果想要强制改变,可以使用@Order 注解。
  • 拦截器
    • 如果使用 order() 方法
java
@Order(2)
@Component
public class MyFilter1 implements Filter {}
java
@Component
public class WebAdapter implements WebMvcConfigurer {
    @Autowired
    MyInterceptor1 myInterceptor1;
    @Autowired
    MyInterceptor2 myInterceptor2;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor1).addPathPatterns("/**").order(2);
        registry.addInterceptor(myInterceptor2).addPathPatterns("/**").order(1);
    }
}

总结

  • 原理实现上:过滤器基于回调实现,而拦截器基于动态代理。
  • 控制粒度上:过滤器和拦截器都能够实现对请求的拦截功能,但是在拦截的粒度上有较大的差异,拦截器对访问控制的粒度更细。
  • 使用场景上:拦截器往往用于权限检查、日志记录等,过滤器主要用于过滤请求中无效参数,安全校验。
  • 依赖容器上:过滤器依赖于 Servlet 容器,局限于 web,而拦截器依赖于 Spring 框架,能够使用 Spring 框架的资源,不仅限于 web。
  • 触发时机上:过滤器在 Servlet 前后执行,拦截器在 handler 前后执行,现在大多数 web 应用基于 Spring,拦截器更细。

更新: 2020-09-15 09:04:46
原文: <https://www.yuque.com/fcant/notes/kzimkp&gt;

最近更新