Spring Boot自定义API异常处理触发@ExceptionHandler歧义报错如何解决
报错根因
这个启动错误的核心是Spring在注册异常处理器时,发现同一个MethodArgumentNotValidException异常类型同时匹配到了两个处理方法,无法判定该优先使用哪一个,直接抛出映射歧义异常。
具体到代码场景,歧义来源来自两处:
- 自定义的、标注了
@ExceptionHandler(MethodArgumentNotValidException.class)的handleMethodArgumentNotValid方法 - 继承的
ResponseEntityExceptionHandler父类中自带的、由@ExceptionHandler(Exception.class)标注的final修饰的handleException兜底方法
后者的匹配范围覆盖所有Exception子类,自然也包含MethodArgumentNotValidException,和自定义的方法形成了双重映射,触发启动报错。
解决方案
按以下几步调整代码即可解决冲突:
- 移除类上重复的注解:
@ControllerAdvice和@RestControllerAdvice功能完全重叠,后者本身就包含前者的能力,因为需要返回JSON格式的异常响应,只保留@RestControllerAdvice即可。 - 修正重写方法的注册逻辑:当前写的
handleMethodArgumentNotValid方法的签名,和ResponseEntityExceptionHandler父类中定义的可重写方法完全一致,本应直接通过重写覆盖父类的默认处理逻辑。但额外给这个方法加了@ExceptionHandler注解,导致这个方法被单独注册成一个独立的异常处理器,打破了原本的重写关联,最终触发映射歧义。调整为去掉方法上的@ExceptionHandler,加上@Override注解明确重写关系即可。 - 全局兜底方法可以正常放开:之前注释掉的处理通用Exception的
handleAll方法可以正常使用,Spring会按照异常匹配范围的窄宽优先级,自动优先使用专门处理MethodArgumentNotValidException的重写方法,不会和通用兜底方法产生冲突。
调整后的可运行代码
@RestControllerAdvice public class CustomRestExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleMethodArgumentNotValid( MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { List<String> errors = new ArrayList<>(); for (FieldError error : ex.getBindingResult().getFieldErrors()) { errors.add(error.getField() + ": " + error.getDefaultMessage()); } for (ObjectError error : ex.getBindingResult().getGlobalErrors()) { errors.add(error.getObjectName() + ": " + error.getDefaultMessage()); } CustomApiError customApiError = new CustomApiError( new Date(), HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors ); return handleExceptionInternal( ex, customApiError, headers, customApiError.getStatus(), request ); } @ExceptionHandler({Exception.class}) public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) { CustomApiError customApiError = new CustomApiError( new Date(), HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred" ); return new ResponseEntity<>(customApiError, new HttpHeaders(), customApiError.getStatus()); } }
补充说明:Spring异常处理器的匹配优先级始终遵循「匹配范围越窄,优先级越高」的规则,只要不额外给重写的父类方法加重复的
@ExceptionHandler注解,就不会出现同类异常多映射的冲突。
内容的提问来源于stack exchange,提问作者Jugal
相关产品推荐
相关产品推荐

