Getting Ambiguous @ExceptionHandler method mapped for MethodArgumentNotValidException while startup of spring boot application
Asked Answered
P

5

49

I have written custom exception handler class for one of my spring controllers to validate if email attribute from request param is in the proper format. So created a new class which extends ResponseEntityExceptionHandler class and wrote a method with @ExceptionHandler.

But during spring boot application startup, I am getting below exception which is stopping to run my project. Could someone help me to resolve this?

Exception during server startup:

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]: {public com.TestVO com.handler.exception.TestExceptionHandler.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}

Custom Class to handle MethodArgumentNotValidException:

@ControllerAdvice
public class ExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public ErrorVO processValidationError(MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();
        FieldError fieldError = fieldErrors.get(0);
        ErrorVO dto = new ErrorVO(fieldError.getDefaultMessage());
        return dto;
    }
}
Prizewinner answered 23/8, 2018 at 18:17 Comment(0)
D
77

The ambiguity is because you have the same method - @ExceptionHandler in both the classes - ResponseEntityExceptionHandler, MethodArgumentNotValidException. You need to write the overridden method as follows to get around this issue -

   @Override
   protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                 HttpHeaders headers, HttpStatus status, WebRequest request) {
          String errorMessage = ex.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
          List<String> validationList = ex.getBindingResult().getFieldErrors().stream().map(fieldError->fieldError.getDefaultMessage()).collect(Collectors.toList());
          LOGGER.info("Validation error list : "+validationList);
          ApiErrorVO apiErrorVO = new ApiErrorVO(errorMessage);
          apiErrorVO.setErrorList(validationList);
          return new ResponseEntity<>(apiErrorVO, status);
   }
Dobson answered 23/8, 2018 at 20:12 Comment(3)
Response i greate, but why not just : @Override protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { List<String> validationList = ex.getBindingResult().getFieldErrors() .stream() .map(fieldError -> fieldError.getDefaultMessage()) .collect(Collectors.toList()); return new ResponseEntity<>(validationList, status); }Lussier
@Override was working properly in Spring 2.0, but I tried to upgrade to Spring 3.0, and, I am not able to @Override this method anymore, is there any solution to that?Endometriosis
@SumitMonapara omit ExceptionHandler in the overriding method.Phallic
B
26

The problem you experience here is to be expected. You're effectively extending Spring ResponseEntityExceptionHandler which by default already contains a declaration for MethodArgumentNotValidException in it's own @ExceptionHandler annotation.

You can easily check on this from the source of the class:

https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java

This is why you see this bit of error in the logs:

java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]

Based on all the above you should remove your own exception handler method and override the ones from it.

Bylaw answered 23/8, 2018 at 18:53 Comment(2)
The method is declared as final, how can you override it?Name
ResponseEntityExceptionHandler#handleMethodArgumentNotValid is protected and thus one can override it. Not sure about which final method you are referring to.Bylaw
B
10

Spring has a built in ResponseEntityExceptionHandler for MethodArgumentNotValidException. So you have two handlers for the same exception which is not allowed.

You can try to override ResponseEntityExceptionHandler.handleMethodArgumentNotValid().

Birdcage answered 23/8, 2018 at 18:52 Comment(0)
K
9

UPDATE for Spring Boot 3.

If you are extending the ResponseEntityExceptionHandler class and override its methods you should not use the @ExceptionHandler annotation.

Otherwise Spring's org.springframework.web.servlet.HandlerExceptionResolver will find more then one handler for the same exception class and report an error.

This is valid also for methods that just handle those exceptions without overriding handler methods. Even with a different signature and/or in a different class not extending ResponseEntityExceptionHandler. The exceptions are listed here:

@ExceptionHandler({
    HttpRequestMethodNotSupportedException.class,
    HttpMediaTypeNotSupportedException.class,
    HttpMediaTypeNotAcceptableException.class,
    MissingPathVariableException.class,
    MissingServletRequestParameterException.class,
    MissingServletRequestPartException.class,
    ServletRequestBindingException.class,
    MethodArgumentNotValidException.class,
    HandlerMethodValidationException.class,
    NoHandlerFoundException.class,
    NoResourceFoundException.class,
    AsyncRequestTimeoutException.class,
    ErrorResponseException.class,
    MaxUploadSizeExceededException.class,
    ConversionNotSupportedException.class,
    TypeMismatchException.class,
    HttpMessageNotReadableException.class,
    HttpMessageNotWritableException.class,
    MethodValidationException.class,
    BindException.class
})

Solution

Remove @ExceptionHandler annotations from the methods handling the same exceptions as in ResponseEntityExceptionHandler (listed above). For these exceptions you should always override the handler methods and not define your own ones.

Kenzie answered 9/1 at 15:20 Comment(1)
This is the complete solution for SpringBoot3, when overriding omit @ExceptionHandler. Thank you!Phallic
H
1

I found a solution to this problem!

I changed the @ControllerAdvice annotation to @RestControllerAdvice for Rest controller.

Spring boot and ControllerAdvice structures already contain MethodArgumentNotValidException. Then I override the MethodArgumentNotValidExcetion function that comes by default by calling override method via intellij idea / or a different ide.

and it worked successfully! :) Hope it helps you! :)

Example

@Override
@ResponseStatus
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

    for (ObjectError methodArgumentNotValidException : ex.getBindingResult().getAllErrors()){
        localerrorEntitiesList.add(new ErrorEntity(methodArgumentNotValidException.getDefaultMessage(),methodArgumentNotValidException.getCode()));
    }

    try {
        return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList),HttpStatus.BAD_REQUEST);
    }catch (Exception e){
        return new ResponseEntity<>(new ErrorResponse(localerrorEntitiesList),HttpStatus.BAD_REQUEST);
    }

}
Hodess answered 23/11, 2022 at 20:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.