排查追踪断层需确认异常是否穿透所有拦截层:检查各拦截器是否静默捕获未重抛、环绕通知是否显式throw ex、代理/异步边界是否导致丢失、traceId是否全程透传、兜底异常处理器是否收不到异常。

排查多层切面拦截器中错误未透传导致的追踪断层,核心在于确认异常是否真正“穿透”所有拦截层,而非被某一层静默拦截、包装丢失原始堆栈,或在异步/代理边界处中断传播。
检查拦截器是否吞掉异常且未重抛
Spring 等框架中,HandlerInterceptor.preHandle 返回 false 会中断流程,但不等于抛出异常;若在 afterCompletion 或环绕通知(@Around)中捕获了异常却只记录、未 throw 或 throw e,就会造成断层。
- 逐个审查各拦截器/切面的
catch块:是否存在空catch{}或仅log.error()后直接返回 - 环绕通知中,确保
proceed()调用被try-catch包裹后,catch分支必须显式throw ex(不是新建异常,除非明确要增强) - 避免用
throw new RuntimeException(ex)替代throw ex——这会切断原始异常的cause链和堆栈
验证异常是否跨代理/异步边界丢失
Spring AOP 默认基于 JDK 动态代理或 CGLIB,若目标方法被声明为 private、final,或调用发生在同一对象内(this.method()),切面根本不会生效;异步方法(@Async)、线程池任务、CompletableFuture 回调中的异常也不会自动冒泡到外层拦截器。
- 检查异常发生点是否在代理对象的方法调用链上(非 this. 调用)
- @Async 方法内抛异常,默认由
AsyncUncaughtExceptionHandler处理,需单独配置日志或转发机制 - CompletableFuture 使用
exceptionally()或handle()时,若未主动 re-throw,异常即终止
给异常打上下文标记并校验透传完整性
在最外层入口(如 Controller 方法开始前)创建唯一 trace ID,并将其注入异常的 suppressed、MDC 或自定义字段;后续每层拦截器在捕获时检查该标识是否存在、是否一致。
- 可在
@Around切面中统一做:if (ex instanceof BusinessException) { ex.addSuppressed(new TraceMarker(traceId)); } - 日志框架(如 Logback)配合 MDC,在进入第一个拦截器时
MDC.put("traceId", id),确保所有日志带相同上下文 - 最外层全局异常处理器中,检查
ex.getStackTrace()是否包含预期的业务类名,以及ex.getCause()是否可逐层回溯至原始位置
启用框架级错误传播验证
利用 Spring 的 DispatcherServlet 错误处理机制作为兜底:若全局异常处理器(@ControllerAdvice)没收到异常,说明它早在拦截器链中就被截断。
- 临时添加一个最简
@ControllerAdvice,只打印所有捕获的异常,确认是否“收不到” - 开启 Spring 日志:
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG,观察异常是否进入processDispatchResult流程 - 对比禁用所有拦截器后的异常行为——若此时能正常捕获并打印完整堆栈,即可锁定问题拦截器

















