#java #spring #spring-boot #exception
Вопрос:
У меня есть контроллер, обрабатывающий некоторые запросы:
@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
, это было бы правдой, поэтому он может даже сначала вызвать меня к этому обработчику, не проверяя другие.
Мои вопросы таковы:
- Кто-нибудь знает, рекомендуется ли добавлять a
@ExceptionHandler(Exception.class)
для обработки общих исключений? Если нет, то как правильно это сделать? - Как я могу гарантировать , что иерархия классов будет соблюдаться (например, если однажды я это сделаю
SomeVerySpecificException extends SomeSpecificException
, как Весна узнает, что она должна позвонить мне поSomeSpecificException
прямому родительскому — доException
— бабушки и дедушки)?
Ответ №1:
После отладки я нашел ответ в их коде — хотя это и не задокументировано, жаль.
@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
и позволяют обрабатывать ошибки по умолчанию - Если содержит только один элемент, они возвращают его
- Если содержит более одного элемента, они сортируют их по глубине (от ближайшего к самому дальнему расширению) и возвращают первый метод.
Таким образом, гарантия не дается по контракту, но она находится в процессе реализации, и кажется, что это действительно хорошо сделано.