如何确保@ExceptionHandler(Exception.class)将在Spring Boot中最后调用?<



我有一个控制器处理一些请求:

@RequestMapping(method = RequestMethod.POST, value = "/endpoint")
public ResponseEntity<MyResponse> myEndpoint(@RequestBody MyRequest request) throws Exception {
//...
}

这样的控制器可能会抛出几个异常,为此我使用@ExceptionHandler这样:

@ExceptionHandler(SomeSpecificException.class)
@ResponseBody
public ResponseEntity<Error> handleSomeSpeficicFailure(SomeSpecificException e) {
//handle specific exception
}
@ExceptionHandler(SomeOtherSpecificException.class)
@ResponseBody
public ResponseEntity<Error> handleSomeOtherSpeficicFailure(SomeOtherSpecificException e) {
//handle specific exception
}
//etc.

如果抛出的异常不属于任何已知的类,我添加了一个通用的Exception.class处理程序,它返回自定义的500:

@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<Error> handleUnknownFailure(Exception e) {
//handle unknown exception
}

做一些测试,它似乎工作良好。如果我抛出一个特定的异常,我将在特定的处理程序中调用,如果我抛出一个未映射的异常,我将在泛型处理程序中调用。

然而,我没有看到任何提及(无论是在JavaDoc还是在Spring文档中)关于保证我将首先调用特定方法,然后调用泛型方法。

如果Spring正在测试一个特定的异常是instanceof Exception,它将是真的,所以它甚至可能首先在这个处理程序上调用我,而不检查其他的。

我的问题是:

  • 有没有人知道添加@ExceptionHandler(Exception.class)来处理通用异常是否良好?如果不是,正确的做法是什么?
  • 我怎么能保证类层次结构将被尊重(例如,如果有一天我有SomeVerySpecificException extends SomeSpecificException, Spring如何知道它必须在SomeSpecificException-直接父级-Exception-祖父级之前调用我)?

经过调试,我在他们的代码中找到了答案——尽管没有文档记录,这很遗憾。

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

基本上对于给定的Exception,它们首先查找所有映射的方法,mappedException.isAssignableFrom(exceptionType)

一旦创建了这个列表,然后:

  • 如果列表为空,它们返回null并让默认的错误处理
  • 如果只包含一个元素,则返回该元素
  • 如果包含多个元素,它们按深度排序(从最近的扩展到最远的扩展)并返回第一个方法。

所以保证不是在合同中给出的,但它在执行中,而且看起来做得很好。

最新更新