filter抛出的异常无法被@controlleradvice捕获,因其位于dispatcherservlet之前;需封装为带cause的自定义异常并原样抛出,使异常穿透至spring mvc异常处理链。

Java中Filter本身不参与Spring MVC的异常处理链,但可以通过主动构造异常链+统一兜底的方式,在Filter层拦截并转化底层崩溃,让错误既不失根因,又能被后续全局处理器识别和收敛。
Filter内捕获异常后必须包装为带cause的业务异常
Filter的doFilter方法里不能裸抛RuntimeException或ServletException,否则会绕过@ControllerAdvice。正确做法是:在catch块中用自定义异常类显式封装原始异常,并传入cause。
- 定义继承RuntimeException的异常类,提供含Throwable参数的构造函数,例如ApiFilterException(String msg, Throwable cause)
- 在Filter中捕获到SQLException、NullPointerException等时,统一转为throw new ApiFilterException("网关校验失败", e),确保e作为cause被保留
- 避免写throw new RuntimeException("校验失败")——这会切断异常链,丢失堆栈上下文
Filter异常需穿透至DispatcherServlet才能被@ControllerAdvice捕获
@ControllerAdvice默认只处理进入HandlerMapping之后的异常。要让Filter抛出的异常“抵达”它,关键在于不提前终止请求生命周期。
- 不要在Filter中调用response.sendError()或request.getRequestDispatcher().forward(),这些操作会跳过Spring MVC主流程
- 必须让异常原样向上抛出(即throw new XxxException(e)),使请求继续进入DispatcherServlet,触发异常解析机制
- 若使用Spring Boot,确保Filter注册方式为@Bean + FilterRegistrationBean,而非@WebFilter,便于纳入Spring容器管理与AOP代理链
配合全局处理器递归提取根因并标准化响应
ControllerAdvice收到Filter抛出的异常后,需主动展开异常链,定位最底层原因,再映射为语义化错误码与状态码。
- 在@ExceptionHandler中调用Throwable.getRootCause()或循环getCause(),找到原始SQLException或FeignException
- 根据根因类型决定HTTP状态码:如SecurityException→401,RemoteServiceException→503,DataAccessException→500
- 响应体中保留完整异常链信息(如code、message、traceId),同时日志记录时用logger.error("Filter异常", e),确保Caused by自动展开
补充:对JVM级崩溃需另设兜底机制
Filter无法拦截真正的JVM崩溃(如OutOfMemoryError、StackOverflowError),这类错误已脱离Java异常体系。需通过JVM参数+外部监控协同防御:
- 添加-XX:+UseGCOverheadLimit -XX:OnOutOfMemoryError="kill -9 %p"等参数实现进程级自愈
- 在启动脚本中配置java -jar app.jar || restart.sh,保证服务可用性
- 结合Prometheus+AlertManager监听JVM指标(heap_used_ratio、thread_count),提前预警
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











