在构建Web应用程序时,我们经常需要在请求到达最终处理逻辑(例如Servlet或Controller)之前或之后执行一些通用操作。这时,过滤器(Filter)和拦截器(Interceptor)就派上了用场。虽然它们都能对请求进行预处理和后处理,但在功能层次、作用范围以及实现方式上存在显著差异。理解这些区别对于合理设计和实现Web应用至关重要。

过滤器(Filter)是什么?

是什么:过滤器是Servlet规范中定义的一种组件,它运行在Web服务器(Servlet容器)接收到请求之后、但在请求送达Servlet或JSP之前。它可以检查、修改请求和响应,或者对请求进行拦截。

为什么需要:过滤器提供了一种标准机制,允许开发者在不修改目标资源(Servlet、JSP等)代码的情况下,实现一些通用的横切关注点功能,例如字符编码统一、请求参数校验、用户身份验证、日志记录、资源压缩等。

哪里起作用:过滤器工作在Servlet容器层面。当一个请求进入Servlet容器时,首先经过一系列过滤器,然后才到达目标Servlet。响应离开Servlet时,也会按相反的顺序经过这些过滤器。

如何工作:过滤器通过实现jakarta.servlet.Filter(或旧版本的javax.servlet.Filter)接口来定义。核心方法是doFilter(ServletRequest request, ServletResponse response, FilterChain chain)。在这个方法中,你可以:

  • 在调用chain.doFilter(request, response)之前执行预处理逻辑。
  • 在调用chain.doFilter(request, response)之后执行后处理逻辑。
  • 修改请求(通过包装ServletRequest)或响应(通过包装ServletResponse)。
  • 直接中断请求,不再向下传递,例如未认证用户重定向到登录页。

过滤器链(FilterChain)是多个过滤器按特定顺序组成的序列。chain.doFilter()方法的作用就是将请求传递给链中的下一个过滤器或目标资源。

拦截器(Interceptor)是什么?

是什么:拦截器通常是Web框架(如Spring MVC、Struts2等)提供的一种机制,它运行在框架的内部,更靠近业务逻辑层。它可以在请求到达Controller之前、Controller处理完请求之后、以及视图渲染之后执行逻辑。

为什么需要:拦截器提供了比过滤器更精细的控制能力,并且通常能够访问框架内部的上下文信息,例如处理当前请求的Handler(Controller方法)、ModelAndView对象等。它适用于框架特定的横切关注点,如权限校验(基于方法级别)、请求参数绑定前的预处理、日志记录(包含更多框架层面的信息)、性能监控(记录请求处理时间)等。

哪里起作用:拦截器工作在Web框架层面。例如在Spring MVC中,拦截器配置在DispatcherServlet之后,在HandlerMapping解析出Handler(Controller方法)之前/之后/视图渲染之后执行。

如何工作:拦截器通过实现框架提供的接口来定义,例如在Spring MVC中是org.springframework.web.servlet.HandlerInterceptor接口。该接口定义了三个核心方法:

  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): 在Controller方法执行之前调用。返回true继续执行后续拦截器和Controller,返回false则中断请求。
  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView): 在Controller方法执行之后、视图渲染之前调用。可以修改ModelAndView对象。
  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): 在整个请求处理完成(包括视图渲染)之后调用,通常用于资源清理。无论Controller是否抛出异常,都会执行此方法。

与过滤器链类似,多个拦截器也会形成一个拦截器链,按配置顺序依次执行。

过滤器和拦截器最本质的区别

它们最核心的区别在于作用的层次和依赖性:

  1. 作用层次:

    • 过滤器:工作在Servlet容器层,对所有进入容器的请求(包括静态资源、Servlet、JSP等)有效。它是在URL模式匹配阶段执行的。
    • 拦截器:工作在Web框架层(如Spring MVC),只对经过框架DispatcherServlet的请求有效,并且通常只拦截对Handler(Controller方法)的调用。
  2. 依赖性:

    • 过滤器:是Servlet规范的一部分,不依赖于任何特定的Web框架。它只能访问标准的Servlet API对象(ServletRequest, ServletResponse)。
    • 拦截器:是Web框架的一部分,依赖于特定的框架。它可以访问框架提供的上下文信息,例如Handler对象、ModelAndView对象等,通常也能访问HttpServletRequestHttpServletResponse(Servlet API的子接口)。

如何选择使用过滤器还是拦截器?(何时使用哪个)

根据它们的特性和作用层次,可以遵循以下原则进行选择:

  • 选择过滤器:

    • 处理所有类型的请求,包括静态资源。
    • 进行与框架无关的通用处理,如字符编码设置、GZIP压缩、跨域处理(CORS)、基本的登录校验(不涉及业务用户体系)、敏感词过滤、图片防盗链等。
    • 需要在请求进入Servlet容器时就进行处理。
  • 选择拦截器:

    • 处理框架内部的请求,特别是与业务逻辑更紧密相关的,只针对Controller/Handler的请求。
    • 需要访问框架内部的对象(如Handler、ModelAndView)或更精细地控制到Controller方法级别。
    • 进行权限校验(涉及用户角色、资源访问)、详细的请求日志记录(包含Controller信息)、性能监控(Controller方法执行时间)、请求参数预处理(与Controller参数绑定相关)等。
    • 需要在Controller执行前、执行后或视图渲染后进行处理。

简而言之:与Servlet容器相关的、通用的、对所有资源都可能生效的用过滤器;与Web框架相关的、针对Controller请求的、需要访问框架内部信息的用拦截器。

多个过滤器和拦截器的执行顺序

多少:一个请求可能会经过零个、一个或多个过滤器,以及零个、一个或多个拦截器。

执行顺序:

  1. 过滤器链执行:请求首先进入Servlet容器,按照配置顺序依次经过过滤器。
  2. 请求进入框架:如果请求匹配了DispatcherServlet,则请求进入Web框架。
  3. 拦截器链执行(preHandle):框架根据配置,按照顺序依次调用拦截器的preHandle方法。如果某个preHandle返回false,整个请求处理中断,后续拦截器和Controller不再执行。
  4. Controller执行:所有preHandle都返回true后,执行目标Controller方法。
  5. 拦截器链执行(postHandle):Controller方法执行后,按照拦截器配置的逆序依次调用postHandle方法(只有preHandle返回true的拦截器才会执行其postHandle)。
  6. 视图渲染:框架进行视图渲染(如果Controller返回了视图)。
  7. 拦截器链执行(afterCompletion):无论是否发生异常,按照拦截器配置的逆序依次调用afterCompletion方法(只有preHandle返回true的拦截器才会执行其afterCompletion)。
  8. 响应经过框架:响应离开Web框架。
  9. 过滤器链执行(post-processing):响应按过滤器配置的逆序依次经过过滤器进行后处理。
  10. 响应返回客户端:响应最终返回给客户端。

需要注意的是,过滤器在请求进入Web框架(DispatcherServlet)之前就已执行完毕。因此,过滤器和拦截器是分处于不同的处理阶段,过滤器总是先于拦截器执行。

如何配置使用过滤器和拦截器?

配置过滤器:

  • 基于部署描述符 (web.xml):web.xml中配置<filter><filter-mapping>标签,指定Filter类、名称、初始化参数以及URL模式。这是传统方式。
  • 基于注解 (@WebFilter): 在Filter类上使用@WebFilter注解,指定URL模式、名称、初始化参数等。这是Servlet 3.0+ 提供的方式,更加便捷。

配置拦截器(以Spring MVC为例):

  • 基于JavaConfig: 实现WebMvcConfigurer接口,重写addInterceptors方法,将自定义的拦截器添加到注册表中,并配置拦截路径和排除路径。
  • 基于XML配置: 在Spring MVC的配置文件中,使用<mvc:interceptors>标签来配置拦截器及其拦截规则。

总结

过滤器和拦截器都是Web开发中实现横切关注点的重要工具。过滤器工作在Servlet容器层,更早介入请求处理,适用于与框架无关的通用任务。拦截器工作在Web框架层,更接近业务逻辑,适用于需要访问框架内部信息或更精细控制Controller请求的场景。它们各自有明确的职责和适用范围,在实际开发中常常被组合使用,以构建功能完善且职责清晰的请求处理流程。


过滤器和拦截器的区别