Настройка приоритета нескольких @ControllerAdvice @ExceptionHandlers
У меня есть классы мультипликаторов, аннотированные с помощью @ControllerAdvice
, каждый из которых имеет метод @ExceptionHandler
.
Один обрабатывает Exception
с намерением, чтобы, если не найден более конкретный обработчик, это должно использоваться.
Печально Spring MVC, кажется, всегда использует наиболее общий случай (Exception
), а не более конкретные (IOException
например).
Это так, как можно было бы ожидать от Spring MVC? Я пытаюсь подражать шаблону от Джерси, который оценивает каждый ExceptionMapper
(эквивалентный компонент), чтобы определить, насколько далеко объявленный тип, который он обрабатывает, - это исключение, которое было выбрано, и всегда использует ближайшего предка.
Ответы
Ответ 1
Это как можно ожидать от Spring MVC?
Как и в случае с Spring 4.3.7, здесь, как ведет себя Spring MVC: он использует HandlerExceptionResolver
экземпляры для обработки исключений, методы обработчика.
По умолчанию конфигурация веб-MVC регистрирует один HandlerExceptionResolver
bean, HandlerExceptionResolverComposite
, который
делегирует список других HandlerExceptionResolvers
.
Эти другие резольверы
-
ExceptionHandlerExceptionResolver
-
ResponseStatusExceptionResolver
-
DefaultHandlerExceptionResolver
зарегистрировано в этом порядке. Для целей этого вопроса мы заботимся только о ExceptionHandlerExceptionResolver
.
An AbstractHandlerMethodExceptionResolver
, который разрешает исключения через методы @ExceptionHandler
.
При инициализации контекста Spring будет генерировать ControllerAdviceBean
для каждого @ControllerAdvice
аннотированный класс, который он обнаруживает. ExceptionHandlerExceptionResolver
будет извлекать их из контекста и сортировать их, используя AnnotationAwareOrderComparator
, который
является расширением OrderComparator
, которое поддерживает Spring Ordered
интерфейса, а также аннотации @Order
и @Priority
, с значение заказа, предоставленное заказным экземпляром, переопределяющим статически определенное значение аннотации (если оно есть).
Затем он зарегистрирует ExceptionHandlerMethodResolver
для каждого из этих экземпляров ControllerAdviceBean
(отображение доступных методов @ExceptionHandler
для типов исключений они предназначены для обработки). Они, наконец, добавляются в том же порядке в LinkedHashMap
(который сохраняет порядок итерации).
Когда возникает исключение, ExceptionHandlerExceptionResolver
будет проходить через эти ExceptionHandlerMethodResolver
и использовать первый, который может обрабатывать исключение.
Итак, вот здесь: если у вас есть @ControllerAdvice
с @ExceptionHandler
для Exception
, который регистрируется перед другим классом @ControllerAdvice
с @ExceptionHandler
для более конкретного исключения, например IOException
, что первый будет вызван. Как уже упоминалось ранее, вы можете контролировать этот порядок регистрации, добавив @ControllerAdvice
аннотированный класс Ordered
или аннотируя его @Order
или @Priority
и присвоить ему соответствующее значение.
Ответ 2
Sotirios Delimanolis очень помог в своем ответе, в ходе дальнейшего расследования мы обнаружили, что в spring 3.2.4 код в любом случае, который ищет аннотации @ControllerAdvice, также проверяет наличие аннотаций @Order и сортирует список ControllerAdviceBeans.
Результирующий заказ по умолчанию для всех контроллеров без аннотации @Order - это Ordered # LOWEST_PRECEDENCE, что означает, что если у вас есть один контроллер, который должен быть самым низким приоритетом, тогда все ваши контроллеры должны иметь более высокий порядок.
Вот пример, показывающий, как иметь два класса обработчика исключений с аннотациями ControllerAdvice и Order, которые могут служить подходящими ответами при возникновении исключения UserProfileException или RuntimeException.
class UserProfileException extends RuntimeException {
}
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
@ExceptionHandler(UserProfileException)
@ResponseBody
ResponseEntity<ErrorResponse> handleUserProfileException() {
....
}
}
@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {
@ExceptionHandler(RuntimeException)
@ResponseBody
ResponseEntity<ErrorResponse> handleRuntimeException() {
....
}
}
- См. ControllerAdviceBean # initOrderFromBeanType()
- См. ControllerAdviceBean # findAnnotatedBeans()
- См. ExceptionHandlerExceptionResolver # initExceptionHandlerAdviceCache()
Наслаждайтесь!
Ответ 3
Порядок обработчиков исключений может быть изменен с помощью аннотации @Order
.
Например:
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomExceptionHandler {
//...
}
@Order
Значение может быть любым целым числом.
Ответ 4
Я также нашел в документации, что:
https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-org.springframework. web.method.HandlerMethod-java.lang. Exception-
ExceptionHandlerMethod
защищенный ServletInvocableHandlerMethod getExceptionHandlerMethod (HandlerMethod handlerMethod, исключительная ситуация исключения)
Найдите метод @ExceptionHandler для данного исключения. Реализация по умолчанию сначала ищет методы в иерархии классов контроллера, и если она не найдена, она продолжает поиск дополнительных методов @ExceptionHandler, предполагая, что были обнаружены некоторые bean-объекты, управляемые @ControllerAdvice Spring. Параметры: handlerMethod - метод, в котором было сгенерировано исключение (может быть нулевым); исключение - сгенерированное исключение. Returns: метод для обработки исключения или ноль.
Таким образом, это означает, что если вы хотите решить эту проблему, вам нужно будет добавить ваш конкретный обработчик исключений в контроллер, выдавая эти исключения. ANd для определения одного и только ControllerAdvice, обрабатывающего глобальный обработчик исключений по умолчанию.
Это упрощает процесс, и нам не нужна аннотация Order для решения проблемы.
Ответ 5
Там похожая ситуация сходится в отличной "" Обработка исключений в Spring MVC "в блоге Spring в разделе озаглавленный Глобальная обработка исключений. Их сценарий включает проверку аннотаций ResponseStatus, зарегистрированных в классе исключений, и, если они есть, повторное исключение, чтобы позволить структуре обрабатывать их. Возможно, вы сможете использовать эту общую тактику - попробуйте определить, может ли быть более подходящий обработчик, и реконструировать.
Кроме того, существуют другие стратегии обработки исключений, которые вы можете посмотреть вместо этого.