@ControllerAdvice无法捕获MethodArgumentNotValidException问题排查
问题描述
我想通过Controller Advice全局捕获异常,创建了如下的ControllerAdvice类,但启动项目时报错。之前已成功捕获多种异常,但捕获MethodArgumentNotValidException时失败,错误信息如下:
自定义异常处理器代码
public class RestExceptionHandler extends ResponseEntityExceptionHandler { List<ErrorEntity> localerrorEntitiesList = new ArrayList<>(); @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) protected ResponseEntity<Object> MethodArgumentNotValidException(MethodArgumentNotValidException methodArgumentNotValidException){ try { for (FieldError methodArgument : methodArgumentNotValidException.getBindingResult().getFieldErrors()) { localerrorEntitiesList.add(new ErrorEntity(methodArgument.getField(),methodArgument.toString())); } return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList),HttpStatus.BAD_REQUEST); }catch (Exception e){ return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList), HttpStatus.BAD_REQUEST); } } }
错误输出
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handlerExceptionResolver' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerExceptionResolver]: Factory method 'handlerExceptionResolver' threw exception; nested exception is java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {protected org.springframework.http.ResponseEntity mywebapp.demowebapp.exception.RestExceptionHandler.MethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception} at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:638) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1352) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1195) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.23.jar:5.3.23] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.23.jar:5.3.23] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.5.jar:2.7.5] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.5.jar:2.7.5] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.5.jar:2.7.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.5.jar:2.7.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.5.jar:2.7.5] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.5.jar:2.7.5] at mywebapp.demowebapp.DemoWebAppApplication.main(DemoWebAppApplication.java:17) ~[main/:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.7.5.jar:2.7.5] Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerExceptionResolver]: Factory method 'handlerExceptionResolver' threw exception; nested exception is java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {protected org.springframework.http.ResponseEntity mywebapp.demowebapp.exception.RestExceptionHandler.MethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception} at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.3.23.jar:5.3.23] at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653) ~[spring-beans-5.3.23.jar:5.3.23] ... 24 common frames omitted Caused by: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {protected org.springframework.http.ResponseEntity mywebapp.demowebapp.exception.RestExceptionHandler.MethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception} at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.addExceptionMapping(ExceptionHandlerMethodResolver.java:114) ~[spring-web-5.3.23.jar:5.3.23] at org.springframework.web.method.annotation.ExceptionHandlerMethodResolver.<init>(ExceptionHandlerMethodResolver.java:78) ~[spring-web-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.initExceptionHandlerAdviceCache(ExceptionHandlerExceptionResolver.java:291) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.afterPropertiesSet(ExceptionHandlerExceptionResolver.java:268) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.addDefaultHandlerExceptionResolvers(WebMvcConfigurationSupport.java:1090) ~[spring-webmvc-5.3.23.jar:5.3.23] at org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.handlerExceptionResolver(WebMvcConfigurationSupport.java:1032) ~[spring-webmvc-5.3.23.jar:5.3.23] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.23.jar:5.3.23] ... 25 common frames omitted Process finished with exit code 0
解决方案
问题根源
报错核心信息:Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException],说明MethodArgumentNotValidException存在两个可匹配的处理方法:
- 你自定义的带
@ExceptionHandler注解的方法 - 父类
ResponseEntityExceptionHandler中的handleException方法(该方法处理Exception类型,自然兼容其子类MethodArgumentNotValidException)
Spring无法判断优先级,因此启动失败。
修复方案
方案1:覆盖父类专属处理方法
ResponseEntityExceptionHandler已内置针对MethodArgumentNotValidException的处理方法handleMethodArgumentNotValid,无需自定义@ExceptionHandler,直接覆盖即可:
@ControllerAdvice public class RestExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { List<ErrorEntity> errorEntitiesList = new ArrayList<>(); for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) { errorEntitiesList.add(new ErrorEntity(fieldError.getField(), fieldError.getDefaultMessage())); } return new ResponseEntity<>(new ErrorResponse(errorEntitiesList), HttpStatus.BAD_REQUEST); } }
方案2:移除父类继承
如果不需要父类的其他异常处理逻辑,可直接使用@ControllerAdvice,保留自定义的异常处理方法:
@ControllerAdvice public class RestExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) { List<ErrorEntity> errorEntitiesList = new ArrayList<>(); for (FieldError fieldError : ex.getBindingResult().getFieldErrors()) { errorEntitiesList.add(new ErrorEntity(fieldError.getField(), fieldError.getDefaultMessage())); } return new ResponseEntity<>(new ErrorResponse(errorEntitiesList), HttpStatus.BAD_REQUEST); } // 其他自定义异常处理方法 }
额外注意事项
- 原代码中
localerrorEntitiesList是类成员变量,多线程环境下会出现线程安全问题,建议改为方法内的局部变量 - 原方法中
@ResponseStatus指定的UNPROCESSABLE_ENTITY与ResponseEntity中的BAD_REQUEST状态码不一致,建议统一状态码
内容的提问来源于stack exchange,提问作者smhylc
相关产品推荐
相关产品推荐

