在 Java Web 开发中拦截机制是一个“看起来简单、实际非常关键”的能力。很多团队在项目早期只把它当成“登录校验工具”但随着系统复杂度增加你会发现几乎所有横切逻辑都离不开它统一鉴权、请求日志、接口限流、幂等控制、参数预处理、XSS 防护、审计追踪、性能监控、异常兜底、跨域处理等。而在 Spring 体系中最常见的两种拦截手段就是Filter和Interceptor。不少开发者初学时会困惑Filter 和 Interceptor 有什么区别都能拦截请求为什么要两套机制登录校验到底放哪一层更合理执行顺序是什么异常会不会影响后续流程在 Spring Boot 项目里怎么正确配置避免“拦不住”或“误拦截”本文将围绕这些核心问题给你一份可落地的实战指南。我们不只讲概念还会结合真实项目中的常见场景讲清楚怎么选、怎么配、怎么避坑。读完后你应该能建立一套完整的拦截机制设计思路。一、先建立全局认识请求在系统里是怎么走的在典型 Spring MVC Web 应用中一个 HTTP 请求大致会经历进入 Servlet 容器Tomcat/Jetty/Undertow先经过 Filter 链进入 DispatcherServlet匹配 HandlerController 方法经过 Interceptor 的 preHandle执行 Controller经过 Interceptor 的 postHandle视图渲染或返回 JSON触发 Interceptor 的 afterCompletion响应返回客户端这个流程决定了一个关键事实Filter 更靠前更底层Interceptor 更靠近 Spring MVC 业务执行层。因此它们并不是互相替代关系而是不同层次的拦截能力。真正好的实践是让两者各司其职。二、Filter 是什么Servlet 规范级别的拦截器Filter过滤器来自 Servlet 规范本质是 Web 容器层面的组件。只要是经过 Servlet 容器的请求它就有机会处理取决于 URL Pattern 配置。Filter 的典型特征与 Spring MVC 无强绑定即使不用 Spring 也能用可以处理 HttpServletRequest 和 HttpServletResponse可以在请求进入业务前和响应返回前做统一处理通过 FilterChain 决定是否放行生命周期由容器管理在 Spring Boot 中可由 Spring 注册和管理常见使用场景编码设置虽然现在多用框架默认配置跨域 CORS 处理也可在 Spring 配置层做请求/响应包装如可重复读 body通用日志埋点、链路 TraceId 注入XSS/SQL 注入等基础防护非业务强相关的网关前置能力你可以把 Filter 理解为“站在大门口的安检层”它不关心你是哪个 Controller只关心请求整体。三、Interceptor 是什么Spring MVC 级别的拦截器Interceptor拦截器是 Spring MVC 提供的扩展机制通常通过实现 HandlerInterceptor 接口来定义。Interceptor 的典型特征强依赖 Spring MVC拦截的是“进入 Controller 的执行过程”可以拿到处理器对象Handler更容易做业务语义相关逻辑有 preHandle、postHandle、afterCompletion 三段生命周期能精细控制路径拦截与排除规则常见使用场景登录态校验、Token 校验权限判断RBAC、接口级权限用户上下文注入如 userId、tenantId接口耗时统计、业务审计幂等校验部分场景多语言、租户信息解析等业务前置处理你可以把 Interceptor 理解为“进入业务楼层后的前台检查”知道你要去哪个房间哪个 Controller。四、Filter 与 Interceptor 核心差异对比下面用实战视角总结关键区别1. 所属层级不同FilterServlet 规范层InterceptorSpring MVC 框架层2. 拦截时机不同Filter 更早执行Interceptor 在 DispatcherServlet 分发后、Controller 前后执行3. 可感知信息不同Filter 不直接感知 Controller 方法信息Interceptor 可获取 Handler可做更细粒度业务判断4. 适用职责不同Filter 适合通用基础能力、协议层处理Interceptor 适合业务相关横切逻辑5. 异常与回调语义不同Filter 通过 doFilter 链控制流程Interceptor 提供三段式回调便于业务前后处理与收尾实际项目中最佳实践通常是基础通用能力放 Filter业务语义拦截放 Interceptor。五、Filter 实战从注册到执行控制在 Spring Boot 中使用 Filter 常见有两种方式WebFilter ServletComponentScanFilterRegistrationBean更推荐便于显式控制这里讲推荐方式。示例定义一个请求日志 FilterjavaComponent public class RequestLogFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req (HttpServletRequest) request; long start System.currentTimeMillis(); String uri req.getRequestURI(); try { chain.doFilter(request, response); } finally { long cost System.currentTimeMillis() - start; System.out.println([Filter] uri uri , cost cost ms); } } }配置注册与顺序javaConfiguration public class FilterConfig { Bean public FilterRegistrationBeanRequestLogFilter requestLogFilterRegistration(RequestLogFilter filter) { FilterRegistrationBeanRequestLogFilter bean new FilterRegistrationBean(); bean.setFilter(filter); bean.addUrlPatterns(/*); bean.setOrder(1); return bean; } }setOrder 很关键。多个 Filter 并存时顺序直接决定执行链路。经验上Trace/日志类靠前安全/校验类次之包装/响应处理类按依赖关系排序六、Interceptor 实战三段生命周期的正确用法实现 HandlerInterceptorjavaComponent public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || token.isBlank()) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录或token缺失\}); return false; } return true; } Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// Controller 执行后、视图渲染前} Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {// 最终收尾适合清理 ThreadLocal} }注册拦截器javaConfiguration public class WebMvcConfig implements WebMvcConfigurer { private final LoginInterceptor loginInterceptor; public WebMvcConfig(LoginInterceptor loginInterceptor) { this.loginInterceptor loginInterceptor; } Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/login, /api/register, /error); } }这段配置体现了 Interceptor 的实战价值可以对业务接口做精细路径控制这正是登录鉴权放在 Interceptor 的重要原因之一。七、执行顺序与调用链面试高频、线上高危点如果同时存在多个 Filter、多个 Interceptor请求执行顺序通常是Filter 按 order 正序进入Interceptor 的 preHandle 按注册顺序执行Controller 执行Interceptor 的 postHandle 逆序执行Interceptor 的 afterCompletion 逆序执行Filter 链退出响应返回如果某个 Interceptor 的 preHandle 返回 false后续 Interceptor 的 preHandle 不再执行Controller 不会执行已执行过 preHandle 的拦截器通常仍会进入 afterCompletion注意实际行为与异常路径需要你自己确保响应正确输出因此拦截失败时响应写出要规范否则前端会收到空响应或非预期格式。八、实战场景拆解到底该用 Filter 还是 Interceptor场景1统一注入 TraceId 到 MDC 日志上下文推荐Filter原因越早注入越好后续所有流程含异常都能打到同一个 TraceId。场景2登录态校验、用户身份识别推荐Interceptor原因这属于业务语义层逻辑且常依赖路径规则、注解规则、Handler 信息。场景3请求体防重复读取包装推荐Filter原因属于底层请求对象包装应该在进入 MVC 前完成。场景4接口级权限如 RequiresRole推荐Interceptor 注解解析或直接上 Spring Security原因需要感知具体 Controller 方法及其注解元信息。场景5跨域处理 CORS可选Filter 或 Spring MVC 全局配置若系统简单Filter 可快速兜底若规则复杂建议用官方配置方式统一管理。九、与 Spring Security 的关系不要重复造轮子很多团队在项目中手写 Interceptor 做认证授权这在小项目没问题但在中大型系统中如果涉及复杂认证方式JWT/OAuth2/SSO细粒度权限控制异常认证流程安全上下文传播建议优先考虑 Spring Security。因为它本质上也是基于过滤器链构建的完整安全框架能力边界远大于手写 Interceptor。实战建议是简单登录校验Interceptor 可用正式安全体系Spring Security 为主Interceptor 做业务补充十、常见坑位与排查思路1. 拦截器不生效常见原因没有注册到 WebMvcConfigurer路径匹配错了尤其是 /**、/* 混淆使用了 excludePathPatterns 把目标接口排除了静态资源与接口路径混在一起导致误判2. Filter 执行了两次可能与转发forward/错误派发error dispatch有关Filter 映射 dispatcherType 配置有关Spring Boot 自动注册与手工注册重复有关3. 在 Interceptor 中读取 body 导致 Controller 读不到请求流默认一次性读取解决需要用 HttpServletRequestWrapper 在 Filter 层先包装缓存。4. ThreadLocal 泄漏在 Interceptor preHandle 放入上下文后必须在 afterCompletion 清理特别是线程复用场景Tomcat 线程池下。5. 异常响应格式不统一在 Filter/Interceptor 直接写响应时容易绕开全局异常处理器导致返回结构不一致。建议统一错误输出模型或尽量抛给统一异常处理层结合实际架构设计。十一、生产级最佳实践清单职责分层明确Filter 做基础通用Interceptor 做业务语义顺序可控Filter order、Interceptor 注册顺序都要显式管理路径规则收敛统一管理白名单避免散落硬编码上下文及时清理ThreadLocal 必须在 finally/afterCompletion 清除响应规范统一拦截失败返回统一 JSON 结构与状态码日志可观测记录关键拦截决策点但避免打印敏感信息性能可评估重逻辑不要堆在拦截层必要时做缓存与短路安全优先复用成熟框架复杂认证授权尽量用 Spring Security测试覆盖对拦截路径、排除路径、异常路径写集成测试灰度与开关关键拦截策略支持配置开关便于紧急回滚十二、一个推荐的企业项目落地方案给一个实用分层模板Filter 1TraceFilter生成/透传 TraceId写 MDCFilter 2RequestWrapperFilter包装 request/response 便于重复读取与审计Filter 3CORSFilter统一跨域头处理或改为配置方式Interceptor 1AuthInterceptor登录态识别、用户上下文注入Interceptor 2PermissionInterceptor接口权限校验Interceptor 3AuditInterceptor记录关键业务操作审计信息GlobalExceptionHandler统一异常响应AOP/Service层承接更细粒度业务日志与性能统计这套组合的核心思想是入口层做“通用基础能力”MVC 层做“业务拦截能力”业务层做“领域逻辑能力”。不要把所有逻辑都塞进 Filter 或 Interceptor。Filter 与 Interceptor 是 Java Web 开发中最基础、也最容易被低估的两种机制。它们都能“拦截请求”但拦截层级、语义边界和最佳使用场景完全不同。真正高质量的工程实践不是争论“谁更强”而是根据架构分层把它们用在最合适的位置Filter靠前、通用、底层、协议与容器相关能力Interceptor贴近业务、贴近 Controller、适合鉴权与业务横切逻辑当你能清晰地划分这两者的职责并把顺序、异常、路径、上下文管理做到规范化时拦截机制就不再只是“登录校验插件”而会成为你系统治理能力的重要基础设施。如果把全文浓缩成一句话那就是在 Java Web 中Filter 决定“请求能否规范进入系统”Interceptor 决定“请求能否按业务规则被处理”。两者协同才是可维护、可扩展、可观测的拦截体系。