Подтвердить что ты не робот

Настройка приоритета нескольких @ControllerAdvice @ExceptionHandlers

У меня есть классы мультипликаторов, аннотированные с помощью @ControllerAdvice, каждый из которых имеет метод @ExceptionHandler.

Один обрабатывает Exception с намерением, чтобы, если не найден более конкретный обработчик, это должно использоваться.

Печально Spring MVC, кажется, всегда использует наиболее общий случай (Exception), а не более конкретные (IOException например).

Это так, как можно было бы ожидать от Spring MVC? Я пытаюсь подражать шаблону от Джерси, который оценивает каждый ExceptionMapper (эквивалентный компонент), чтобы определить, насколько далеко объявленный тип, который он обрабатывает, - это исключение, которое было выбрано, и всегда использует ближайшего предка.

4b9b3361

Ответ 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, зарегистрированных в классе исключений, и, если они есть, повторное исключение, чтобы позволить структуре обрабатывать их. Возможно, вы сможете использовать эту общую тактику - попробуйте определить, может ли быть более подходящий обработчик, и реконструировать.

Кроме того, существуют другие стратегии обработки исключений, которые вы можете посмотреть вместо этого.