41

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;
    }
}
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49

4 Answers4

72

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);
   }
  • 1
    Response i greate, but why not just : `@Override protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { List validationList = ex.getBindingResult().getFieldErrors() .stream() .map(fieldError -> fieldError.getDefaultMessage()) .collect(Collectors.toList()); return new ResponseEntity<>(validationList, status); }` – Tomasz Feb 24 '21 at 08:28
24

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.

akortex
  • 5,067
  • 2
  • 25
  • 57
  • The method is declared as final, how can you override it? – Rahul Feb 02 '23 at 18:59
  • `ResponseEntityExceptionHandler#handleMethodArgumentNotValid` is protected and thus one can override it. Not sure about which `final` method you are referring to. – akortex Feb 03 '23 at 11:55
7

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().

KevKosDev
  • 811
  • 2
  • 9
  • 31
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);
    }

}
smhylc
  • 41
  • 1
  • 9