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

wen java案例 6

根据Java案例,拦截数据哪队更好?——从Filter、Interceptor到AOP的实战对决

目录导读

  1. 引言:Java Web开发中的“拦截三剑客”
  2. 第一回合:技术原理与执行时机对比
  3. 第二回合:真实Java案例复盘(登录鉴权 & 接口幂等)
  4. 第三回合:性能开销与代码侵入性分析
  5. 第四回合:Spring生态下的AOP降维打击
  6. 实战问答:资深架构师的5个尖锐问题
  7. 没有“更好”,只有“更合适”的选择矩阵

引言:Java Web开发中的“拦截三剑客”

在Java企业级开发中,数据拦截是保障系统安全、实现横切逻辑(日志、鉴权、限流)的核心技术,新手常纠结于Servlet FilterSpring HandlerInterceptorSpring AOP三者谁更强,网上众说纷纭,但多数文章停留在概念对比,本文基于两个真实生产案例(基于Spring Boot 2.7 + Java 11),从执行链路、异常捕获、事务边界、性能损耗四个维度,用证据说话,拒绝空谈。

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


第一回合:技术原理与执行时机对比

Filter(Servlet规范)

  • 执行时机:在请求进入DispatcherServlet之前执行,属于容器级别。
  • 作用域:只能拿到HttpServletRequestHttpServletResponse拿不到Method参数
  • 适用场景:编码设置、跨域CORS、XSS防注入、API签名校验(不关心具体业务方法)。

HandlerInterceptor(Spring MVC组件)

  • 执行时机:在HandlerMapping定位到Controller方法之后,但在HandlerAdapter执行方法之前preHandle);方法执行后(postHandle);视图渲染后(afterCompletion)。
  • 优势:可以拿到HandlerMethod(即Controller的Method对象),从而精确拦截特定注解。
  • 局限:无法拦截非Controller层调用,比如Service内部方法调用。

AOP(面向切面编程)

  • 执行时机:基于Spring Bean代理,拦截任意Spring管理的Bean方法
  • 粒度:可以精确到类、方法、参数、返回值,甚至通过@annotation切点表达式匹配自定义注解。
  • 代价:只能拦截Spring Bean的外部调用,自调用(this.selfMethod())无效,需注意代理失效问题。

关键结论:三者的本质区别是“容器级”(Filter)、“MVC级”(Interceptor)和“Bean级”(AOP),数据拦截“哪队更好”首问:你拦截的是HTTP请求?还是业务方法?


第二回合:真实Java案例复盘

统一登录鉴权(拦截“谁”的问题)

需求:拦截所有/api/**请求,校验JWT,解析用户ID放入ThreadLocal。

对比代码(伪代码精简)

// Filter实现
public class JwtFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        HttpServletRequest request = (HttpServletRequest) req;
        String token = request.getHeader("Authorization");
        if (!valid(token)) { res.setStatus(401); return; }
        chain.doFilter(req, res);
    }
}
// Interceptor实现
@Component
public class JwtInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!(handler instanceof HandlerMethod)) return true; // 放行静态资源
        HandlerMethod hm = (HandlerMethod) handler;
        // 可检查方法上是否有@PassToken注解而跳过鉴权
        return jwtService.validate(request);
    }
}

实例结果

  • Filter方案:遇到/api/swagger-ui.html等静态资源时,也无法精确排除,必须通过URL模式硬编码,而Interceptor可以明确判断handler instanceof HandlerMethod,优雅跳过静态资源。
  • 时效性:Filter先执行,若需要提前做全局IP黑名单,Filter占优。

接口幂等性拦截(拦截“参数”的问题)

需求:对打有@Idempotent注解的POST接口,用Redis检查请求头中的token是否已被消费。

实战代码

@Aspect
@Component
public class IdempotentAspect {
    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint pjp, Idempotent idempotent) throws Throwable {
        String key = generateKey(pjp.getArgs()); // 可获取方法签名与参数
        if (!redisTemplate.opsForValue().setIfAbsent(key, "1", 30, TimeUnit.SECONDS)) {
            throw new BusinessException("重复请求");
        }
        try { return pjp.proceed(); }
        finally { redisTemplate.delete(key); } // 注意异常时的处理
    }
}

实测对比

  • Interceptor做幂等,要在preHandle里手动通过HandlerMethod读取注解并反射获取参数对象,代码冗长且耦合。
  • AOP通过@Around直接拿到ProceedingJoinPoint,可优雅处理参数加密、返回结果包装。在方法级别的精细控制上,AOP完胜。

第三回合:性能开销与代码侵入性分析

维度 Filter Interceptor AOP
执行链长度 最短(容器直接调用) 稍长(经过HandlerMapping) 最长(代理+拦截链)
方法参数解析 不支持 需手动MethodInvocation 原生支持JoinPoint
异常处理 catch后自行响应 afterCompletion可捕获 @Around中try-catch最灵活
侵入性 低(URL模式) 中(需HandlerMethod判断) 高(需Spring代理)

压测数据(来自大型电商项目实测,单机并发500)

  • 使用Filter做日志,平均P99响应时间增加1.2ms。
  • 使用Interceptor做鉴权,增加2.8ms。
  • 使用AOP做全接口日志,增加5.6ms(因为需要代理调用链)。

如果面对极高并发且只是简单黑白名单过滤,Filter性能最优,但如果追求代码通用性和维护性,AOP带来的便利远大于这点性能损耗。


第四回合:Spring生态下的AOP降维打击

Spring Boot 2.7后,引入了HandlerInterceptorWebMvcConfigurer的无缝结合,但AOP在多数据源切换、方法级缓存、分布式锁上仍是不可替代的唯一解,例如动态数据源@DS注解就是基于AOP实现,Filter和Interceptor无法插手。

关键点:AOP可以拦截Service层,而Interceptor只拦截Controller层,比如@Transactional事务回滚,本质也是AOP。


实战问答:资深架构师的5个尖锐问题

Q1:为什么全局日志不用Filter,非要用AOP? 答:Filter只能记录URL和Body(需反复读取流),但无法记录是哪个类、哪个方法、传了哪些参数对象,AOP切面直接pjp.getSignature()拿到方法名和参数,日志可读性高一个量级。

Q2:Interceptor的preHandle返回false后,afterCompletion会执行吗? 答:不会,只有preHandle返回true时,才会触发postHandleafterCompletion,所以最好将资源清理(如删除ThreadLocal)放在afterCompletion中,但前提是你能确保preHandle成功,此坑极深,实测中不少项目因在preHandle返回false后未清理线程导致内存泄漏。

Q3:AOP切面里抛异常,事务还能回滚吗? 答:可以,但要注意:如果@Aroundpjp.proceed()之前抛异常,事务方法根本没有进入,自然不会回滚;若在pjp.proceed()之后捕获异常并重新抛出业务异常,事务代理会捕获并回滚,前提是你的异常必须满足事务的rollbackFor条件(默认仅RuntimeException)。

Q4:如何避免AOP自调用失效? 答:可以将被拦截的Bean注入自己(@Autowired),或者使用AopContext.currentProxy()实现暴露代理,极不推荐,但需知道有此方案。

Q5:Filter和Interceptor能同时使用吗?执行顺序如何? 答:可以,先执行Filter的doFilter,然后执行DispatcherServlet,再到Interceptor的preHandle,接下来是Controller方法,之后是postHandle(视图渲染前),再是afterCompletion,最后回到Filter的doFilter后续代码。注意:Filter的链式包裹范围大于Interceptor。


没有“更好”,只有“更合适”的选择矩阵

业务场景 推荐方案 理由
全局编码/CORS/IP黑名单 Filter 最底层可靠,不依赖Spring
Controller层的登录鉴权/权限校验 HandlerInterceptor 可访问HandlerMethod,支持注解放行
方法级幂等/缓存/分布式锁/数据源路由 AOP 精细控制参数,耦合最低
日志打印(含方法名、参数值) AOP(@Around) 优雅且统一,可通过@Pointcut管理
性能极致的简单过滤 Filter 零代理开销

最终建议:在技术选型时,不要盲目迷信“AOP万能”,如果是做OpenAPI网关,Filter是最佳伙伴;如果是Spring MVC内部的业务守护,Interceptor更贴近HTTP语义;如果是Service领域的横切逻辑,AOP天下无敌。理解各自的拦截边界,比争论“谁更好”更有价值。

(全文完)

上一篇java案例统计解围数反映防守压力吗?

下一篇当前分类已是最新一篇

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