Issue
I have 2 exception handler classes annotated with @RestControllerAdvice
and:
I use the first one as global exception handler to catch exceptions:
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@Override
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
protected ResponseEntity<Object> handleMethodArgumentNotValid(...) {
// ...
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<Object> handleAllUncaughtException(Exception ex, WebRequest request) {
// ...
}
// code omitted for clarity
}
and the second for validation exceptions (I creates custom validation):
@RestControllerAdvice
public class ValidationExceptionHandler { // did not extend from extends ResponseEntityExceptionHandler
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
protected ValidationErrorResponse onConstraintValidationException(ConstraintViolationException e) {
// ...
}
}
When I move onConstraintValidationException
to GlobalExceptionHandler
class, I catch validation exception and display corresponding message. But when it is in the second ControllerAdvice class (ValidationExceptionHandler
) as shown above, code does not hit onConstraintValidationException
method.
I also tried to extend the second class from ResponseEntityExceptionHandler
, but does not make any sense.
So, what is the problem and how can I fix it?
Solution
In your case it could be question of priorities... The first exception handler has highest priority, and probably the handleAllUncaughtException is going to capture all the exceptions.
For catching the ConstraintViolationException in the second handler (ValidationExceptionHandler) you should give to it the highest priority, like so:
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ValidationExceptionHandler {
and to the first one lowest:
@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
Can have a look at this discussion, it can be interesting to learn more about how it works and have some alternative solutions: Setting Precedence of Multiple @ControllerAdvice @ExceptionHandlers
Edit: in case you need something more flexible or create a more complex hierarchy of handlers, you can use simple integers in the Order annotation. Lowest values, highest priority. So something like this can work as well:
@RestControllerAdvice
@Order(-1)
public class ValidationExceptionHandler {
//...
@RestControllerAdvice
@Order(0)
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
//...
In the Ordered source code you have:
public interface Ordered {
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
In the Order annotation you can see the default is LOWEST_PRECEDENCE (Integer.MAX_VALUE):
public @interface Order {
/**
* The order value.
* <p>Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}
Answered By - cdr89
Answer Checked By - Willingham (JavaFixing Volunteer)