拦截器 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/**"); // 放行登录页与静态资源
}
}
对比速查表
| 维度 | Filter | HandlerInterceptor |
|---|---|---|
| 归属 | 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。