我们经常使用 @RestControllerAdvice 配合 @ExceptionHandler 去拦截控制器的异常,有时候我们需要一个比较抽象的异常去拦截那些我们意料之外的情况,比如 RuntimeException 。但是有时候会发现并非按照所预料的顺序拦截,我们需要的应该是最匹配的 Advice 优先。

@RestControllerAdvice 源码里有一句注释: All such beans are sorted based on {@link org.springframework.core.Ordered Ordered} semantics or {@link org.springframework.core.annotation.Order @Order} 。也就是说我们定义的 @RestControllerAdvice 类,会通过 @Order 注解或者 Ordered 接口进行排序。因此要指定顺序,只需要把同一(抽象)级别的异常 handler(@ExceptionHandler)放在一个类里,然后通过 @Order 指定顺序。

具体的异常拦截,指定大的优先级(值小)。

@Order(Integer.MIN_VALUE)
@RestControllerAdvice
public class ParameterBindingExceptionHandler {
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse handleHttpMessageNotReadableException() {
        return new ErrorResponse(ErrorCodeEnum.INVALID_PARAMETER.getCode(), "Invalid parameter.");

抽象的异常拦截,指定小的优先级(值大,默认最大因此省略)

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorResponse handleRuntimeException(RuntimeException e) {
        ExceptionHandlerMethodResolver
        log.error("Catch an unprocessed exception.", e);
        return new ErrorResponse(ErrorCodeEnum.INTERNAL_ERROR.getCode(), ErrorCodeEnum.INTERNAL_ERROR.getMessage());

这样就会先去匹配 ParameterBindingExceptionHandler 再去匹配 GlobalExceptionHandler。