configuración de la precedencia de @ExceptionHandlers @ControllerAdvice múltiples


41

tengo clases multipler con anotada @ControllerAdvice, cada uno con un método @ExceptionHandler en.

Uno maneja Exception con la intención de que si no se encuentra un controlador más específica, esto se debe utilizar.

Sadly Spring MVC parece estar siempre utilizando el caso más genérico (Exception) en lugar de los más específicos (IOException por ejemplo).

¿Es así como se esperaría que se comportase Spring MVC? Intento emular un patrón de Jersey, que evalúa cada ExceptionMapper (componente equivalente) para determinar qué tan lejos se maneja el tipo declarado que maneja de la excepción que se ha lanzado, y siempre usa el antecesor más cercano.

51

¿Es así como se esperaría que se comportase Spring MVC?

A partir de la primavera 4.3.7, así es como se comporta Spring MVC: utiliza HandlerExceptionResolver casos de manejar excepciones lanzadas por los métodos de controlador.

Por defecto, la configuración MVC web registra una sola HandlerExceptionResolver frijol, un HandlerExceptionResolverComposite, que

delegados a una lista de otros HandlerExceptionResolvers.

Esos otros son resolutores

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

registrados en ese orden. A los fines de esta pregunta, solo nos importa ExceptionHandlerExceptionResolver.

Un AbstractHandlerMethodExceptionResolver que resuelve excepciones través de @ExceptionHandler métodos.

En el contexto de inicialización, Primavera generará una ControllerAdviceBean para cada clase anotada @ControllerAdvice detecta. El ExceptionHandlerExceptionResolver recuperará éstos a partir del contexto, y ordenarlos mediante el uso de AnnotationAwareOrderComparator cuales

es una extensión de OrderComparator que soporta Ordered interfaz de primavera, así como los @Order y @Priority anotaciones, con un valor fin proporcionada por una Se ha ordenado la instancia anulando un valor de anotación definido estáticamente (si existe).

Va a continuación, registrar un ExceptionHandlerMethodResolver para cada uno de estos casos (ControllerAdviceBean de mapeo disponibles @ExceptionHandler métodos para los tipos de excepción que están hechas para soportar). Estos finalmente se agregan en el mismo orden a LinkedHashMap (que conserva el orden de iteración).

Cuando ocurre una excepción, el ExceptionHandlerExceptionResolver iterará a través de estos ExceptionHandlerMethodResolver y usará el primero que pueda manejar la excepción.

Así que el punto aquí es: si tiene un @ControllerAdvice con un @ExceptionHandler para Exception que obtiene registrado antes de que otra clase @ControllerAdvice con un @ExceptionHandler una excepción más específica, como IOException, que primero será llamado. Como se mencionó anteriormente, puede controlar esa orden de registro haciendo que su clase anotada @ControllerAdvice implemente Ordered o anotándola con @Order o @Priority y dándole un valor apropiado.


2

Hay una situación similar en la excelente publicación "Exception Handling in Spring MVC" en el blog de Spring, en la sección titulada Global Exception Handling. Su escenario implica verificar las anotaciones de ResponseStatus registradas en la clase de excepción, y si están presentes, volver a lanzar la excepción para permitir que el marco las maneje. Es posible que pueda utilizar esta táctica general: trate de determinar si existe un controlador más apropiado y reiniciando.

Como alternativa, hay otras estrategias de manejo de excepciones cubiertas que puede ver en su lugar.


58

Sotirios Delimanolis fue muy útil en su respuesta, en la investigación posterior encontramos que, en la primavera 3.2.4 de todos modos, el código que busca las anotaciones @ControllerAdvice también comprueba la presencia de anotaciones @Order y ordena la lista de ControllerAdviceBeans .

El orden predeterminado resultante para todos los controladores sin la anotación @Order es Ordenado # LOWEST_PRECEDENCE, lo que significa que si tiene un controlador que necesita la prioridad más baja, TODOS los controladores deben tener un orden superior.

Aquí hay un ejemplo que muestra cómo tener dos clases de controlador de excepción con ControllerAdvice y anotaciones de pedido que pueden servir las respuestas adecuadas cuando se produce una excepción de perfil de usuario o una excepción de tiempo de ejecución.

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() { 
     .... 
    } 
} 
  • Ver ControllerAdviceBean # initOrderFromBeanType()
  • Ver ControllerAdviceBean # findAnnotatedBeans()
  • Ver ExceptionHandlerExceptionResolver # initExceptionHandlerAdviceCache()

disfrutar!


15

El orden de los manejadores de excepciones se puede cambiar utilizando la anotación @Order.

Por ejemplo:

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 's valor puede ser cualquier número entero.


1

También encontré en la documentación que:

ExceptionHandlerMethod

protegida ServletInvocableHandlerMethod getExceptionHandlerMethod (HandlerMethod handlerMethod, excepción Excepción)

encontrar un método para la @ExceptionHandler dado excepción. La implementación predeterminada busca métodos en la jerarquía de la clase del controlador primero y si no se encuentra, continúa buscando métodos @ExceptionHandler adicionales asumiendo algunos @ControllerAdvice Los beans gestionados por Spring se detectaron. Parámetros: handlerMethod - el método en el que se produce la excepción (puede ser nulo) excepción - los devuelve una excepción planteadas: un método para controlar la excepción o nula

Así que esto significa que si se quiere resolver En este caso, deberá agregar su controlador de excepciones específico dentro del controlador lanzando esas excepciones. Y para definir uno y solo ControllerAdvice que maneja el manejador global de excepciones por defecto.

Esto simplifica el proceso y no necesitamos la anotación de la orden para manejar el problema.