自定义ApiException无法被@ControllerAdvice异常处理器捕获的问题
我一眼就看到问题所在了——你的Controller层没有正确传递Service抛出的检查型异常,导致JVM把它包装成了一个RuntimeException子类,你的全局处理器自然抓不到目标异常。
先看关键的Controller代码:
@DeleteMapping("/{subjectTypeId}") public ResponseEntity<Object> deleteSubjectType(@PathVariable int subjectTypeId) { subjectTypeService.deleteSubjectType(subjectTypeId); return ResponseEntity.ok().body(null); }
你的Service方法deleteSubjectType明确声明了throws ApiException(这是一个检查型异常,因为它继承自Exception而非RuntimeException),但Controller调用它的时候,既没有用try-catch捕获处理,也没有在方法上声明throws ApiException。这种情况下,Java会自动把这个检查型异常包装成UndeclaredThrowableException(属于RuntimeException的子类)抛出,而你的全局异常处理器只监听ApiException类型,当然匹配不上。
两种快速解决方法,选一种适合你的:
方法1:让Controller向上抛出ApiException
直接在Controller方法上声明throws ApiException,把异常传递给全局处理器:
@DeleteMapping("/{subjectTypeId}") public ResponseEntity<Object> deleteSubjectType(@PathVariable int subjectTypeId) throws ApiException { subjectTypeService.deleteSubjectType(subjectTypeId); return ResponseEntity.ok().body(null); }
这样抛出的就是原始的ApiException,全局处理器的@ExceptionHandler(ApiException.class)就能精准捕获。
方法2:把ApiException改为非检查型异常
如果不想在每个Controller方法上都写throws声明,更简便的方式是让ApiException继承RuntimeException(非检查型异常,不需要显式声明抛出):
public class ApiException extends RuntimeException{ // 修改父类为RuntimeException private final String message; private final HttpStatus httpStatus; public ApiException(String message, HttpStatus httpStatus) { this.message = message; this.httpStatus = httpStatus; } @Override public String getMessage() { return message; } public HttpStatus getHttpStatus() { return httpStatus; } }
修改后,Service和Controller都不需要再写throws ApiException,抛出的异常会直接被全局处理器捕获,这也是Spring项目中自定义异常的常用做法。
最后提个小细节:你的全局异常处理器方法handleExceptions不需要加static修饰符,Spring会把GeneralExceptionHandler作为Bean实例化,调用实例方法更符合Spring的设计习惯,建议去掉static。
内容的提问来源于stack exchange,提问作者Leo

