拦截器 HandlerInterceptor 与过滤器 Filter 的区别与配合

Servlet 有 Filter,Spring MVC 有 HandlerInterceptor,两者都能在请求处理前后插入逻辑,位置和作用域却不一样。区分它们,是配置编码过滤器、登录拦截、访问日志时的基本功。

一张图看位置

客户端 → Filter(容器层) → DispatcherServlet → HandlerInterceptor.preHandle
        → Controller 方法 → HandlerInterceptor.postHandle → 视图渲染
        → HandlerInterceptor.afterCompletion → 响应回客户端
  • Filter 属于 Jakarta Servlet 规范,在 DispatcherServlet 之前/之后执行,拿不到 Spring MVC 的 HandlerMethod。
  • HandlerInterceptor 属于 Spring MVC,挂在 HandlerExecutionChain 上,能拿到具体将要执行的处理器方法(HandlerMethod)、ModelAndView。

Filter:Servlet 层的通用拦截

实现 jakarta.servlet.Filter,适合编码设置、跨域兜底、压缩、粗粒度鉴权等与 MVC 无关的事:

public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        chain.doFilter(request, response); // 放行,交给下一个 Filter / DispatcherServlet
    }
}

注册:Spring Boot 用 FilterRegistrationBean;原生 Spring MVC 在 WebApplicationInitializer 或 web.xml 里注册:

// AbstractAnnotationConfigDispatcherServletInitializer 子类中
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    super.onStartup(servletContext);
    jakarta.servlet.FilterRegistration.Dynamic encoding =
            servletContext.addFilter("encoding", new EncodingFilter());
    encoding.addMappingForUrlPatterns(null, false, "/*"); // 对所有 URL 生效
}

HandlerInterceptor:三个钩子方法

public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        if (request.getSession().getAttribute("loginUser") == null) {
            response.sendRedirect("/login");   // 未登录:跳登录页
            return false;                      // 返回 false 短路,不再往下执行
        }
        return true;                            // 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        // 处理器已执行、视图渲染前:可向 modelAndView 补公共数据
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        // 请求结束(含异常)必然回调:记日志、释放资源
    }
}

语义要点:preHandle 返回 false 则后续全部跳过;postHandle 在异常时不保证执行;afterCompletion 只要 preHandle 放行了就一定会执行(finally 语义),因此日志清理放这里。

注册拦截器与路径控制

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")                 // 拦截所有路径
                .excludePathPatterns("/login", "/css/**", "/js/**"); // 放行登录页与静态资源
    }
}

对比速查表

维度FilterHandlerInterceptor
归属Servlet 规范(jakarta.servlet)Spring MVC(spring-webmvc)
执行位置DispatcherServlet 前后处理器调用前后
能否拿 HandlerMethod/ModelAndView不能
受 Spring 容器管理默认不经过 Spring(除非注册为 Bean)是,可注入 Service 等
典型用途编码、压缩、CORS 兜底登录鉴权、权限、日志、公共模型

配合使用的建议

  • 与容器/协议相关的(字符编码、跨域、压缩)放 Filter。
  • 与业务/MVC 相关的(登录态校验、操作日志带 HandlerMethod 信息、给视图补公共数据)放 Interceptor。
  • 同一需求两边都出现时,Filter 先于 Interceptor 执行;多个 Filter 按注册顺序执行。

Filter 管「协议与管线」,Interceptor 管「业务与 MVC」。分层各司其职,配置不乱;记住登录拦截这类需要 Spring Bean 与路径精细化控制的场景首选 HandlerInterceptor。

笔记加载中…