Spring 应用中的 HandlerInterceptor 和 Filter

1、概览

本文将会带你了解 Spring MVC HandlerInterceptor 和 Servlet Filter 之间的区别和各自的应用场景。

2、Filter

Filter 是 Web 服务器的一部分,而不是 Spring 框架的组件。对于传入请求,可以使用 Filter 来操作甚至阻止请求到达任何 Servlet。反之亦然,也可以阻止响应到达客户端。

Spring Security 就是使用 Filter 进行身份认证和授权的一个很好的例子。要配置 Spring Security,只需添加一个 Filter,即 DelegatingFilterProxy。这样,Spring Security 就能拦截所有进出流量。这就是 Spring Security 可以在 Spring MVC 之外使用的原因。

2.1、创建 Filter

创建一个实现 jakarta.servlet.Filter 接口的 Filter 实现类:

public class LogFilter implements Filter {

    private Logger logger = LoggerFactory.getLogger(LogFilter.class);

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
      throws IOException, ServletException {
        logger.info("Hello from: " + request.getLocalAddr());
        chain.doFilter(request, response); // 继续请求链调用
    }

}

接下来,覆写 doFilter 方法,在该方法中,可以访问或操作 ServletRequestServletResponseFilterChain 对象。可以使用 FilterChain 对象允许或阻止请求。

可以选择直接继承 HttpFilter 抽象类,覆写其 doFilter 方法。

这样的话不需要自己强制转换 ServletRequestServletResponseHttpServletRequestHttpServletResponse

protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)

然后可以在配置类中通过 FilterRegistrationBean Bean 进行注册:

@Bean
public FilterRegistrationBean<LogFilter> logFilter() {
    FilterRegistrationBean<LogFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new LogFilter()); // 设置 Filer 实现
    registrationBean.setOrder(2);				// Filter 的执行顺序
    registrationBean.addUrlPatterns("/*");		// Filter 拦截的 URL
    return registrationBean;
}

3、HandlerInterceptor

HandlerInterceptor 是 Spring MVC 框架中的组件,位于 DispatcherServlet 和 Controller 之间。使用拦截器,可以在请求到达 Controller 之前、视图渲染之前和之后对其进行拦截。

3.1、创建 HandlerInterceptor

创建一个实现 org.springframework.web.servlet.HandlerInterceptor 接口的拦截器类。

可以选择覆写三个方法:

  • preHandle():在调用目标 Handler 之前执行,如果该方法返回 false 则会阻止请求调用。
  • postHandle():在调用目标 Handler 之后、DispatcherServlet 渲染视图之前执行。
  • afterCompletion():在请求完成、视图渲染后执行。

创建一个测试拦截器,覆写上述三个方法,用于输出日志:

public class LogInterceptor implements HandlerInterceptor {

    private Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
      throws Exception {
        logger.info("preHandle");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 
      throws Exception {
        logger.info("postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 
      throws Exception {
        logger.info("afterCompletion");
    }

}

然后,通过 WebMvcConfigurer 配置类,配置 LogInterceptor

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor()) // 设置拦截器实现
            .addPathPatterns("/**")     // 设置要拦截的路径
            .excludePathPatterns("/foo")    // 设置要忽略拦截的路径
            ;            
    }
}

4、主要区别和使用场景

FilterHandlerInterceptor 在请求/响应流程中的位置示意图如下:

Filter 和 HandlerInterceptor 在请求/响应流程中的位置示意图

Filter 可在请求到达 DispatcherServlet 之前对其进行拦截,因此非常适合执行粗粒度任务,例如

  • 认证。
  • 日志记录和审计。
  • 压缩图像和数据。
  • 任何希望与 Spring MVC 解耦的功能。

HandlerInterceptor 会拦截 DispatcherServlet 与 Controller 之间的请求。这是在 Spring MVC 框架内完成的,提供了对 HandlerModelAndView 对象的访问。这减少了重复代码,并允许实现更细粒度的功能,例如:

  • 处理横切关注点,如应用日志记录。
  • 详细的权限检查。
  • 操作 Spring Context 或 Model。

5、总结

本文介绍了如何创建、使用 Servlet 过滤器(Filter)和 Spring MVC 拦截器(HandlerInterceptor),以及它们之间的区别和应用场景。


Ref:https://www.baeldung.com/spring-mvc-handlerinterceptor-vs-filter