Czy tak będzie się zachowywać Spring MVC?
Począwszy od wersji Spring 4.3.7, tak zachowuje się Spring MVC: używa on HandlerExceptionResolver
instancji do obsługi wyjątków generowanych przez metody obsługi.
Domyślnie konfiguracja sieci Web MVC rejestruje pojedynczy HandlerExceptionResolver
komponent bean HandlerExceptionResolverComposite
, który
delegaci na listę innych HandlerExceptionResolvers
.
Te inne resolwery są
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
zarejestrowane w tej kolejności. Na potrzeby tego pytania tylko nam zależy ExceptionHandlerExceptionResolver
.
AbstractHandlerMethodExceptionResolver
Że postanawia wyjątki poprzez @ExceptionHandler
metod.
Podczas inicjalizacji kontekstu Spring wygeneruje ControllerAdviceBean
dla każdej @ControllerAdvice
wykrytej klasy z adnotacjami. ExceptionHandlerExceptionResolver
Pobierze je z kontekstu i sortować je za pomocą za pomocą AnnotationAwareOrderComparator
którego
jest rozszerzeniem OrderComparator
obsługującym Ordered
interfejs Springa, a także adnotacje @Order
i @Priority
, z wartością zamówienia dostarczoną przez instancję Ordered przesłaniającą statycznie zdefiniowaną wartość adnotacji (jeśli istnieje).
Następnie zarejestruje ExceptionHandlerMethodResolver
dla każdego z tych ControllerAdviceBean
wystąpień (mapowanie dostępnych @ExceptionHandler
metod na typy wyjątków, które mają obsługiwać). Są one ostatecznie dodawane w tej samej kolejności do a LinkedHashMap
(co zachowuje kolejność iteracji).
Gdy wystąpi wyjątek, ExceptionHandlerExceptionResolver
will iteruje przez nie ExceptionHandlerMethodResolver
i używa pierwszego, który może obsłużyć wyjątek.
Chodzi o to, że jeśli masz a @ControllerAdvice
z @ExceptionHandler
for, Exception
który zostanie zarejestrowany przed inną @ControllerAdvice
klasą z @ExceptionHandler
bardziej szczegółowym wyjątkiem, na przykład IOException
, zostanie wywołany ten pierwszy. Jak wspomniano wcześniej, możesz kontrolować kolejność rejestracji, @ControllerAdvice
implementując klasę z Ordered
adnotacjami lub dodając do niej adnotację @Order
lub @Priority
i nadając jej odpowiednią wartość.
@ExceptionHandler
metod w ramach a@ControllerAdvice
, wybierana jest ta, która obsługuje najbardziej szczegółową nadklasę rzuconego wyjątku.Sotirios Delimanolis był bardzo pomocny w udzieleniu odpowiedzi, po dalszych badaniach stwierdziliśmy, że wiosną 3.2.4 kod wyszukujący adnotacje @ControllerAdvice również sprawdza obecność adnotacji @Order i sortuje listę ControllerAdviceBeans.
Wynikowa domyślna kolejność dla wszystkich kontrolerów bez adnotacji @Order to Ordered # LOWEST_PRECEDENCE, co oznacza, że jeśli masz jeden kontroler, który musi mieć najniższy priorytet, WSZYSTKIE kontrolery muszą mieć wyższą kolejność.
Oto przykład pokazujący, jak mieć dwie klasy obsługi wyjątków z adnotacjami ControllerAdvice i Order, które mogą obsługiwać odpowiednie odpowiedzi, gdy wystąpi wyjątek UserProfileException lub 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() { .... } }
Cieszyć się!
źródło
Kolejność obsługi wyjątków można zmienić za pomocą
@Order
adnotacji.Na przykład:
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
Wartość może być dowolną liczbą całkowitą.źródło
W dokumentacji znalazłem również, że:
https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-.springframework. web.method.HandlerMethod-java.lang.Exception-
Oznacza to, że jeśli chcesz rozwiązać ten problem, musisz dodać swoją określoną procedurę obsługi wyjątków w kontrolerze rzucającym te wyjątki. ANd, aby zdefiniować jeden i jedyny ControllerAdvice obsługujący globalną domyślną procedurę obsługi wyjątków.
Upraszcza to proces i nie potrzebujemy adnotacji do zamówienia, aby rozwiązać problem.
źródło
Podobna sytuacja została przedstawiona w doskonałym poście „ Obsługa wyjątków w Spring MVC ” na blogu Spring, w sekcji zatytułowanej „ Obsługa wyjątków globalnych” . Ich scenariusz obejmuje sprawdzenie, czy adnotacje ResponseStatus zarejestrowane w klasie wyjątku, i jeśli są obecne, ponowne zgłoszenie wyjątku, aby platforma mogła je obsłużyć. Możesz użyć tej ogólnej taktyki - spróbuj określić, czy istnieje bardziej odpowiedni przewodnik i ponownie rzuć.
Alternatywnie, istnieje kilka innych strategii obsługi wyjątków, którym możesz się przyjrzeć.
źródło
Ważna klasa do obsłużenia:
**@Order(Ordered.HIGHEST_PRECEDENCE)** public class FunctionalResponseEntityExceptionHandler { private final Logger logger = LoggerFactory.getLogger(FunctionalResponseEntityExceptionHandler.class); @ExceptionHandler(EntityNotFoundException.class) public final ResponseEntity<Object> handleFunctionalExceptions(EntityNotFoundException ex, WebRequest request) { logger.error(ex.getMessage() + " " + ex); ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false),HttpStatus.NOT_FOUND.toString()); return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND); } }
Inne wyjątki o niskim priorytecie
@ControllerAdvice public class GlobalResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { private final Logger logger = LoggerFactory.getLogger(GlobalResponseEntityExceptionHandler.class); @ExceptionHandler(Exception.class) public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request) { logger.error(ex.getMessage()+ " " + ex); ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.toString(), request.getDescription(false),HttpStatus.INTERNAL_SERVER_ERROR.toString()); } }
źródło
możesz również użyć wartości liczbowej, jak poniżej
@Order(value = 100)
źródło