Spring Boot 全局异常捕获 ControllerAdvice 无法捕获 过滤器(Filter)和拦截器(Interceptor)中的异常

Spring Boot 全局异常捕获 ControllerAdvice 无法捕获 过滤器(Filter)和拦截器(Interceptor)中的异常

💡 原文中文,约2400字,阅读约需6分钟。
📝

内容提要

在Spring Boot中,使用@ControllerAdvice和@RestControllerAdvice来处理异常。但是,自定义过滤器中的异常无法被捕获。解决方法是在过滤器中捕获异常,并将异常对象设置到Attribute属性中,然后在新建的接收异常的Controller中重新抛出异常。这样,ControllerAdvice就能捕获到异常了。

🔎

延伸解读

异常处理的作用范围与执行顺序

Spring Boot 中,请求处理链的顺序是过滤器、拦截器、ControllerAdvice。ControllerAdvice 只能捕获 Controller 层及其下层(Service、DAO)抛出的异常,因为异常必须传播到 Controller 层才会被 ControllerAdvice 处理。如果异常在过滤器或拦截器中抛出,请求流程会提前终止,无法到达 ControllerAdvice,因此无法被捕获。理解这一顺序是解决问题的关键。

过滤器异常捕获的变通方案

为了捕获过滤器中的异常,可以在过滤器中用 try-catch 捕获异常,将其存入 request 的 Attribute 中,然后通过 RequestDispatcher 将请求转发到一个特定的 Controller。该 Controller 从 Attribute 中取出异常并重新抛出,这样异常就进入了 Controller 层,从而能被 ControllerAdvice 捕获。这种方法利用了请求转发机制,将异常处理延迟到 Controller 阶段。

方案适用性与注意事项

该方案适用于过滤器中的异常需要统一处理的情况,但需注意:转发请求可能改变原始请求的路径和方法,需确保不会引发额外问题;同时,在 Controller 中重新抛出异常时,需保证异常类型与 ControllerAdvice 中定义的一致。此外,拦截器中的异常同样无法被 ControllerAdvice 捕获,可参考类似思路处理。

❓

Q&A

为什么Spring Boot的@ControllerAdvice无法捕获过滤器中的异常?

因为请求执行顺序是过滤器 -> 拦截器 -> ControllerAdvice,异常在到达ControllerAdvice之前就已抛出,导致ControllerAdvice无法捕获。

Spring Boot中过滤器、拦截器和ControllerAdvice的执行顺序是怎样的?

执行顺序为:过滤器(Filter)最先执行,然后是拦截器(Interceptor),最后是ControllerAdvice。

如何让@ControllerAdvice捕获过滤器抛出的异常?

在过滤器中用try-catch捕获异常,将异常对象设置到request的Attribute中,并通过RequestDispatcher转发到特定的Controller,在该Controller中重新抛出异常,这样ControllerAdvice就能捕获。

在过滤器中捕获异常后,如何将异常传递给ControllerAdvice?

将异常对象设置到ServletRequest的Attribute中(如setAttribute("filter.error", e)),然后使用request.getRequestDispatcher("/error/exthrow").forward(request, response)转发到指定Controller,在该Controller中从Attribute取出异常并重新抛出。

ControllerAdvice能捕获哪些层的异常?

ControllerAdvice可以捕获Controller层、Service层和DAO层的异常,但无法捕获在Controller层之前(如过滤器和拦截器)抛出的异常。

Token过期时抛出的ExpiredJwtException为什么无法被全局异常处理器捕获?

因为该异常是在自定义过滤器中抛出的,而过滤器执行在ControllerAdvice之前,异常未到达ControllerAdvice就被抛出,所以无法被捕获。

🏷️

标签

➡️

继续阅读