根据java案例,拦截数据哪队更好?

wen java案例 2

本文目录导读:

根据java案例,拦截数据哪队更好?

  1. 目录导读
  2. 开篇问答:为什么Java开发者总在“拦截”上纠结?
  3. 核心概念:两者的“血缘”与“阶层”
  4. 基于真实Java案例的对比实验(Spring Boot 2.7 + 鉴权场景)
  5. 关键维度深度分析(核心对比表)
  6. 实战决策指南:什么业务选谁?
  7. 总结与最佳实践黄金法则
  8. 高频面试/架构设计问答

Java实战对决:拦截器(Interceptor)与过滤器(Filter)谁才是数据拦截的王者?

目录导读

  1. 开篇问答:为什么Java开发者总在“拦截”上纠结?
  2. 核心概念:过滤器(Filter)与拦截器(Interceptor)的底层逻辑差异
  3. 基于真实Java案例的对比实验(Spring Boot + 鉴权场景)
  4. 关键维度深度分析:执行时机、依赖注入、作用域、异常处理
  5. 实战决策指南:什么业务选谁?附代码级证据
  6. 总结与最佳实践黄金法则
  7. 高频面试/架构设计问答

开篇问答:为什么Java开发者总在“拦截”上纠结?

Q1:我用了Spring Boot,为什么还要区分Filter和Interceptor?直接每个请求里写判断不行吗? A:写判断没问题,但那是“过程式”代码,在企业级系统中,横切关注点(如日志、鉴权、防XSS)必须与业务解耦,Filter属于Servlet规范,Interceptor属于Spring MVC组件,两者拦截阶段不同、权限不同、能力不同,选错不仅导致性能浪费,还会出现“拦截到了但拿不到Controller参数”的诡异Bug。

Q2:很多博客说“Filter先执行,Interceptor后执行”,但我的日志顺序总是反的? A:这触及核心盲区,Filter在Servlet容器层(Tomcat)包裹一切,而Interceptor在Spring的DispatcherServlet内部,如果配置了@ControllerAdvice或异步处理,顺序可能因链路变化,下文案例会还原真实时序。


核心概念:两者的“血缘”与“阶层”

过滤器(Filter)——Servlet的“看门大爷”

  • 源自javax.servlet.Filter,由Servlet容器(Tomcat/Jetty)管理。
  • 作用域:覆盖所有能被容器匹配的URL,包括静态资源、JSP、REST接口
  • 特点:拿不到Controller的方法名、参数注解,只能拿到HttpServletRequest/Response原始对象。
  • 生命周期:随容器启动初始化,随容器关闭销毁。

拦截器(Interceptor)——Spring MVC的“交警”

  • 源自org.springframework.web.servlet.HandlerInterceptor,由Spring IoC容器管理。
  • 作用域:仅拦截DispatcherServlet映射到的HandlerMethod静态资源不经过(除非配置)。
  • 特点:可以拿到目标Controller类和方法对象,可访问HandlerMethod参数注解。
  • 典型接口:preHandle(前置)、postHandle(后置,视图渲染前)、afterCompletion(。

基于真实Java案例的对比实验(Spring Boot 2.7 + 鉴权场景)

案例需求

  • 所有/api/**请求必须校验Token。
  • 请求处理完成后记录耗时及用户ID。
  • 校验失败返回401 JSON。

实现A:纯Filter实现

@Component
public class AuthFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        String token = request.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            ((HttpServletResponse) resp).setStatus(401);
            return;
        }
        // 解析Token存到ThreadLocal(注意线程池传递)
        chain.doFilter(req, resp);
    }
}

实现B:纯Interceptor实现

@Component
public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {
        HandlerMethod method = (HandlerMethod) handler;
        // 可获取@RequireAuth等注解,甚至做权限级校验
        String token = req.getHeader("Authorization");
        if (token == null || !token.startsWith("Bearer ")) {
            resp.setStatus(401);
            return false;
        }
        // 将用户ID放入ThreadLocal
        return true;
    }
    @Override
    public void afterCompletion(...) {
        // 清ThreadLocal防内存泄漏
    }
}

实验关键现象

  1. 当请求打到/api/user时,Filter先输出filter-start,随后Interceptor输出interceptor-pre,Controller返回后,Interceptor输出post,最后Filter输出filter-end
  2. 但若请求路径是/js/app.js且未配置静态资源放行,Filter依然会拦截并执行Token校验(除非排除),而Interceptor完全不会触发——因为没有对应的HandlerMethod。
  3. 异常处理差异:Filter中抛异常不会走@RestControllerAdvice全局异常,而Interceptor的preHandle失败后若抛异常,会进入Spring的异常解析器。

关键维度深度分析(核心对比表)

维度 Filter Interceptor 决策优先级
执行时机 容器级,在DispatchServlet之前 处理器映射器找到方法后,执行之前/之后 若需在Spring解析参数之前做处理,必须用Filter
依赖注入 可以通过@Autowired注入,但生命周期比Spring Bean更早,需注意初始化顺序 本身就属于Spring容器,注入自然,推荐优先 复杂业务校验推荐Interceptor
作用范围 所有URL(包括静态、错误页) 仅匹配到HandlerMethod的请求 静态资源需放行时选Interceptor
获取方法级信息 无法获取 HandlerMethod可获取类/方法/注解/参数名 做权限粒度控制时Interceptor完胜
对请求体/响应体处理 可以包装HttpServletRequest(如读取Body后重新放回) 无法直接包装ServletInputStream(需要过滤器配合) 防XSS/解密Body场景必须Filter
异常处理 需自行try-catch,不会走@ExceptionHandler preHandle返回false不会继续,但抛异常会走HandlerExceptionResolver 全局异常统一用Interceptor更优雅
性能开销 极少(Servlet原生) 多一层Spring类型转换和handler匹配 高并发简单校验选Filter

实战决策指南:什么业务选谁?

场景1:CORS跨域配置、字符编码、压缩响应

  • 必选Filter,因为需要最早介入,且针对容器层面,CORS预检OPTIONS请求不会被Spring MVC拦截。

场景2:统计API耗时、用户登录状态校验、权限注解校验

  • 首选Interceptor,因为要访问HandlerMethod拿到@RequireRole("admin")注解,且能利用Spring的@Async等特性。

场景3:请求体内容解密、防止重复提交(流只能读一次)

  • Filter + 包装器,拦截器拿到的是已经解析好的对象,若需要密文流,必须用Filter里的HttpServletRequestWrapper缓存流。

场景4:多模块异构系统(如纯JSP + REST混用)

  • Filter更稳,避免漏掉非Spring管理的资源。

总结与最佳实践黄金法则

  1. 法则一:优先考虑Interceptor作为业务拦截入口,它符合Spring生态,可测试性强,能利用HandlerMethod做精细化控制。
  2. 法则二:当涉及协议层/容器层需求(如CORS、XSS过滤、Gzip解压、静态资源拦截)时,回归Filter
  3. 法则三:两者可共存,但多次重复校验会浪费性能,建议Filter只做“粗粒度安检”,Interceptor做“细粒度业务闸机”。
  4. 法则四:警惕异步请求,对CallableDeferredResult,Interceptor的afterCompletion可能提前执行,此时需要配置AsyncHandlerInterceptor

高频面试/架构设计问答

Q:面试官问“如果Filter和Interceptor都配置了,谁先抛异常?” A:Filter抛异常直接由容器处理,不经过Spring异常解析;若Interceptor的preHandle抛异常,Spring会尝试匹配@ExceptionHandlerHandlerExceptionResolver,所以Filter异常更原始,Interceptor异常更“Spring化”

Q:如何做接口幂等性校验?哪个更适合? A:幂等性需要读取请求体或Token,且需要忽略静态资源,推荐Filter + 包装器读取Body并存入Redis,因为Filter能包装原始流,且对于不经过Spring的URL也能生效。

Q:Logback的MDC放入TraceID,放哪一层最好? A:Interceptor的preHandle最合适,因为能拿到HandlerMethod信息拼接到日志,且可以在afterCompletion中清除,若放Filter,丢失方法名等上下文。


最终结论:没有绝对“更好”,只有“更合适”,如果你的业务逻辑依赖Spring的注解和方法参数,拦截器(Interceptor)是王者;如果涉及协议解析、流重读或静态资源防护,过滤器(Filter)是基石,经验丰富的老手通常同时使用,Filter负责“容器级卫生”,Interceptor负责“业务级执法”,两者协作且不重复劳动,才能构建坚不可摧的数据防线。

抱歉,评论功能暂时关闭!