目录
  1. 源码解读
    1. 调用栈
    2. 核心代码
    3. 结论
ExceptionHandler的执行顺序

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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异常,经过一步步调试,发现调用栈如下:

image-20190326180205336

核心代码

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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,然后对其进行排序,取深度最小的那个(即匹配度最高的那个)。

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

image-20190327224336509

结论

源码不长,我们也可以很容易地就找到我们想要的答案——ExceptionHandler的处理顺序是由异常匹配度来决定的,且我们也无法通过其他途径指定顺序。

文章作者: 谷河
文章链接: https://www.lyytaw.com/spring/ExceptionHandler%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG