129 votes

Définition de la priorité des gestionnaires @ExceptionHandlers multiples de @ControllerAdvice

J'ai plusieurs classes annotées avec @ControllerAdvice, chacune avec une méthode @ExceptionHandler.

L'une gère l' Exception avec l'intention que si aucun gestionnaire plus spécifique n'est trouvé, celui-ci doit être utilisé.

Malheureusement, Spring MVC semble toujours utiliser le cas le plus générique (Exception) plutôt que des cas plus spécifiques (IOException par exemple).

Est-ce ainsi que l'on s'attend à ce que Spring MVC se comporte? J'essaie d'imiter un modèle de Jersey, qui évalue chaque ExceptionMapper (composant équivalent) pour déterminer à quelle distance le type déclaré qu'il gère est de l'exception qui a été lancée, et utilise toujours l'ancêtre le plus proche.

196voto

Sotirios Delimanolis Points 77933

Est-ce ainsi que l'on s'attend à ce que Spring MVC se comporte?

À partir de Spring 4.3.7, voici comment Spring MVC se comporte : il utilise des instances de HandlerExceptionResolver pour gérer les exceptions lancées par les méthodes du gestionnaire.

Par défaut, la configuration web MVC enregistre un seul bean HandlerExceptionResolver, un HandlerExceptionResolverComposite, qui

dédoit à une liste d'autres HandlerExceptionResolvers.

Ces autres résolveurs sont

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

enregistrés dans cet ordre. Pour le besoin de cette question, nous nous intéressons uniquement à ExceptionHandlerExceptionResolver.

Un AbstractHandlerMethodExceptionResolver qui résout les exceptions à travers les méthodes @ExceptionHandler.

Lors de l'initialisation du contexte, Spring générera un ControllerAdviceBean pour chaque classe annotée avec @ControllerAdvice qu'il détecte. L'ExceptionHandlerExceptionResolver récupérera ceux-ci du contexte, et les triera en utilisant AnnotationAwareOrderComparator qui

est une extension de OrderComparator qui prend en charge l'interface Ordered de Spring ainsi que les annotations @Order et @Priority, une valeur d'ordre fournie par une instance Ordered écrasant une valeur d'annotation définie statiquement (le cas échéant).

Il enregistrera ensuite un ExceptionHandlerMethodResolver pour chacune de ces instances de ControllerAdviceBean (associant les méthodes @ExceptionHandler disponibles aux types d'exception qu'elles sont censées gérer). Enfin, ceux-ci sont ajoutés dans le même ordre à un LinkedHashMap (qui préserve l'ordre d'itération).

Lorsqu'une exception se produit, l'ExceptionHandlerExceptionResolver parcourra ces ExceptionHandlerMethodResolver et utilisera le premier qui peut gérer l'exception.

Le point ici est le suivant : si vous avez un @ControllerAdvice avec un @ExceptionHandler pour Exception qui est enregistré avant une autre classe @ControllerAdvice avec un @ExceptionHandler pour une exception plus spécifique, comme IOException, le premier sera appelé. Comme mentionné précédemment, vous pouvez contrôler cet ordre d'enregistrement en faisant en sorte que votre classe annotée @ControllerAdvice implémente Ordered ou en l'annotant avec @Order ou @Priority et en lui donnant une valeur appropriée.

124voto

Dominic Clifton Points 106

Sotirios Delimanolis a été très utile dans sa réponse, après vérification approfondie, nous avons constaté que, dans spring 3.2.4 de toute façon, le code qui recherche les annotations @ControllerAdvice vérifie également la présence des annotations @Order et trie la liste des ControllerAdviceBeans.

L'ordre par défaut résultant pour tous les contrôleurs sans l'annotation @Order est Ordered#LOWEST_PRECEDENCE, ce qui signifie que si vous avez un contrôleur qui doit être la plus basse priorité, alors TOUS vos contrôleurs doivent avoir un ordre plus élevé.

Voici un exemple montrant comment avoir deux classes de gestionnaire d'exceptions avec des annotations ControllerAdvice et Order qui peuvent servir des réponses appropriées lorsque survient une UserProfileException ou une RuntimeException.

class UserProfileException extends RuntimeException {
}

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
    @ExceptionHandler(UserProfileException)
    @ResponseBody
    ResponseEntity handleUserProfileException() {
        ....
    }
}

@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {

    @ExceptionHandler(RuntimeException)
    @ResponseBody
    ResponseEntity handleRuntimeException() {
        ....
    }
}
  • Voir ControllerAdviceBean#initOrderFromBeanType()
  • Voir ControllerAdviceBean#findAnnotatedBeans()
  • Voir ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache()

Profitez!

28voto

Koraktor Points 9532

L'ordre des gestionnaires d'exceptions peut être modifié en utilisant l'annotation @Order.

Par exemple:

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 {

    //...

}

La valeur de @Order peut être n'importe quel entier.

6voto

Mathieu Stn Points 168

J'ai également trouvé dans la documentation que :

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

protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception)

Trouver une méthode @ExceptionHandler pour l'exception donnée. L'implémentation par défaut recherche les méthodes dans la hiérarchie de classes du contrôleur d'abord et si elles ne sont pas trouvées, elle continue à rechercher d'autres méthodes @ExceptionHandler en supposant que certains beans Spring @ControllerAdvice ont été détectés. Paramètres : handlerMethod - la méthode où l'exception a été déclenchée (peut être nulle) exception - l'exception déclenchée Renvoie : une méthode pour gérer l'exception, ou null

Cela signifie donc que si vous voulez résoudre ce problème, vous devrez ajouter votre gestionnaire d'exceptions spécifique au sein du contrôleur lançant ces exceptions. Et définir un unique ControllerAdvice pour gérer le gestionnaire d'exceptions par défaut global.

Cela simplifie le processus et nous n'avons pas besoin de l'annotation Order pour gérer le problème.

5voto

Vicky Points 97

Vous pouvez également utiliser une valeur numérique, comme ci-dessous

@Order(value = 100)

Les valeurs plus basses ont une priorité plus élevée. La valeur par défaut est *{@code Ordered.LOWEST_PRECEDENCE}, indiquant la priorité la plus basse (perdant face à toute autre * valeur d'ordre spécifiée)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X