ExceptionHandler的执行顺序

在项目开发中常常会遇到统一异常处理的问题,在springMVC中有一种解决方式,使用ExceptionHandler。举个例子,java

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseBody
    public Result handleIllegalArgumentException(IllegalArgumentException e) {
        logger.error(e.getLocalizedMessage(), e);
        return Result.fail(e.getMessage());
    }

    @ExceptionHandler({RuntimeException.class})
    @ResponseBody
    public Result handleRuntimeException(RuntimeException e) {
        logger.error(e.getLocalizedMessage(), e);
        return Result.failure();
    }
}
复制代码

在这段代码中,咱们能够看到存在两个异常处理的函数分别处理IllegalArgumentException和RuntimeException,可是转念一想,就会想到一个问题,IllegalArgumentException是RuntimeException的子类,那么对IllegalArgumentException这个异常又会由谁来处理呢?起初在网上看到一些答案,能够经过Order设置,可是通过简单的测试,发现Order并不起任何做用。虽然心中已有猜想,但仍是但愿可以找到真正能够证实想法的证据,因而便尝试找到这一块的源码。算法

源码解读

调用栈

排出掉缓存的状况,主动触发一个IllegalArgumentException异常,通过一步步调试,发现调用栈以下:spring

image-20190326180205336

核心代码

决定最终选择哪一个ExceptionHandler的核心代码为ExceptionHandlerMethodResolver的getMappedMethod方法。代码以下:缓存

private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
  List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
  for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
    if (mappedException.isAssignableFrom(exceptionType)) {
      matches.add(mappedException);
    }
  }
  if (!matches.isEmpty()) {
    Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
    return this.mappedMethods.get(matches.get(0));
  }
  else {
    return null;
  }
}
复制代码

这个首先找到能够匹配异常的全部ExceptionHandler,而后对其进行排序,取深度最小的那个(即匹配度最高的那个)。app

至于深度比较器的算法以下图,就是作了一个简单的递归,不停地判断父异常是否为目标异常来取得最终的深度。函数

image-20190327224336509

结论

源码不长,咱们也能够很容易地就找到咱们想要的答案——ExceptionHandler的处理顺序是由异常匹配度来决定的,且咱们也没法经过其余途径指定顺序(其实也没有必要)。测试

相关文章
相关标签/搜索