Spring中自定义@ControllerAdvice全局异常处理未触发问题排查
你遇到的核心问题有两个:一是全局异常处理器没触发,二是WebMvcTest中无法正确验证。咱们一步步拆解:
为什么你的RestErrorResponseExceptionHandler没被触发?
ResponseEntityExceptionHandler这个父类只负责处理Spring MVC框架抛出的特定异常(比如HttpRequestMethodNotSupportedException、MethodArgumentNotValidException、HttpMessageNotReadableException等),它对自定义异常、普通RuntimeException这类非MVC框架异常是没有默认处理逻辑的。
你只重写了handleExceptionInternal方法,但这个方法是父类中各个具体异常处理方法(比如handleMethodArgumentNotValid)的最终回调——如果某个异常根本没被父类的任何处理方法捕获,handleExceptionInternal根本不会被调用。
另外,Spring的异常处理优先级确实是:控制器内部的@ExceptionHandler > 全局@ControllerAdvice处理器,但你的问题根源不是优先级,而是你的处理器根本没声明要捕获那些你抛出的异常。
修复全局异常处理器的两种方案
方案1:添加自定义@ExceptionHandler捕获所有(或指定)异常
如果想让这个处理器接管所有异常(包括自定义异常、RuntimeException),直接在处理器类上添加@ExceptionHandler注解,指定要捕获的异常类型,然后在方法里返回自定义响应:
@Order(Ordered.HIGHEST_PRECEDENCE) @ControllerAdvice public class RestErrorResponseExceptionHandler extends ResponseEntityExceptionHandler { // 捕获所有未被处理的异常 @ExceptionHandler(Exception.class) public ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) { HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR; return ResponseEntity.status(status).body(Error.from(status)); } // 保留对Spring MVC异常的自定义处理,重写父类方法 @Override protected ResponseEntity<Object> handleExceptionInternal( Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) { // 不需要调用super,直接返回自定义响应 return ResponseEntity.status(status).body(Error.from(status)); } }
这样不管是MVC框架异常还是业务异常,都会被这个处理器捕获并返回你定义的Error对象。
方案2:重写父类对应异常的处理方法(仅处理MVC框架异常)
如果只需要处理Spring MVC自带的异常,那你需要重写父类中对应异常的处理方法,而不是只重写handleExceptionInternal。比如处理请求参数校验异常:
@Order(Ordered.HIGHEST_PRECEDENCE) @ControllerAdvice public class RestErrorResponseExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleMethodArgumentNotValid( MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { // 自定义响应体 return ResponseEntity.status(status).body(Error.from(status)); } // 按需重写其他MVC异常的处理方法,比如handleHttpRequestMethodNotSupported等 }
这样每个你需要处理的MVC异常,都通过重写对应的方法来返回自定义响应,handleExceptionInternal可以作为通用回调,但前提是异常已经被某个具体的handle方法捕获。
解决WebMvcTest的验证问题
@WebMvcTest默认只会扫描测试目标控制器及其相关的Web层组件,你的全局异常处理器可能没被自动加载。解决方法有两种:
- 在
@WebMvcTest中显式导入你的异常处理器:
@WebMvcTest(YourRestController.class) @Import(RestErrorResponseExceptionHandler.class) public class YourControllerTest { // 测试代码 }
- 如果你的处理器和控制器在同一个包下,或者Spring Boot的组件扫描能覆盖到它,也可以用
@SpringBootTest配合@AutoConfigureMockMvc来测试(但这个会加载整个Spring上下文,比@WebMvcTest重一些):
@SpringBootTest @AutoConfigureMockMvc public class YourControllerTest { // 测试代码 }
这样测试时就能正确触发全局异常处理器,返回预期的Error对象了。
内容的提问来源于stack exchange,提问作者ioreskovic

