如何确保 @ExceptionHandler(Exception.class) 在 Spring Boot 中最后被调用?
How to make sure that @ExceptionHandler(Exception.class) will be called last in 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
处理程序,其中 returns 自定义 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
- 直接 parent - 在 Exception
- grandparent)? 之前
经过调试,我在他们的代码中找到了答案——虽然没有记录,但很遗憾。
@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)
.
的所有映射方法
创建此列表后,然后:
- 如果列表为空,他们return
null
并让默认的错误处理
- 如果只包含一个元素,他们return它
- 如果包含多个元素,他们按深度排序(从最近到最远),return 第一种方法。
所以保证不是合同上的,而是在执行中,看起来确实做得很好。
我有一个控制器处理一些请求:
@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
处理程序,其中 returns 自定义 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
- 直接 parent - 在Exception
- grandparent)? 之前
经过调试,我在他们的代码中找到了答案——虽然没有记录,但很遗憾。
@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)
.
创建此列表后,然后:
- 如果列表为空,他们return
null
并让默认的错误处理 - 如果只包含一个元素,他们return它
- 如果包含多个元素,他们按深度排序(从最近到最远),return 第一种方法。
所以保证不是合同上的,而是在执行中,看起来确实做得很好。