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
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
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.