¿Es así como se esperaría que se comporte Spring MVC?
A partir de Spring 4.3.7, así es como se comporta Spring MVC: usa HandlerExceptionResolver
instancias para manejar excepciones lanzadas por métodos de controlador.
De forma predeterminada, la configuración de MVC web registra un solo HandlerExceptionResolver
bean, a HandlerExceptionResolverComposite
, que
delegados a una lista de otros HandlerExceptionResolvers
.
Esos otros resolutores son
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
registrado en ese orden. A los efectos de esta pregunta solo nos preocupamos ExceptionHandlerExceptionResolver
.
Una AbstractHandlerMethodExceptionResolver
que resuelve excepciones a través de @ExceptionHandler
métodos.
En la inicialización del contexto, Spring generará un ControllerAdviceBean
para cada @ControllerAdvice
clase anotada que detecte. Los ExceptionHandlerExceptionResolver
recuperará del contexto y los ordenará usando el AnnotationAwareOrderComparator
que
es una extensión de OrderComparator
que admite la Ordered
interfaz de Spring, así como las anotaciones @Order
y @Priority
, con un valor de orden proporcionado por una instancia Ordenada que anula un valor de anotación definido estáticamente (si lo hubiera).
Luego registrará un ExceptionHandlerMethodResolver
para cada una de estas ControllerAdviceBean
instancias (mapeando los @ExceptionHandler
métodos disponibles a los tipos de excepción que deben manejar). Estos finalmente se agregan en el mismo orden a a LinkedHashMap
(que conserva el orden de iteración).
Cuando ocurre una excepción, ExceptionHandlerExceptionResolver
iterará a través de estos ExceptionHandlerMethodResolver
y usará el primero que pueda manejar la excepción.
Entonces, el punto aquí es: si tiene una @ControllerAdvice
con una @ExceptionHandler
para, Exception
se registra antes que otra @ControllerAdvice
clase con una @ExceptionHandler
para una excepción más específica, como IOException
, se llamará a la primera. Como se mencionó anteriormente, puede controlar ese orden de registro haciendo que su @ControllerAdvice
clase anotada implemente Ordered
o anotándola con @Order
o @Priority
y dándole un valor apropiado.
@ExceptionHandler
métodos dentro de a@ControllerAdvice
, se elige el que maneja la superclase más específica de la excepción lanzada.Sotirios Delimanolis fue muy útil en su respuesta, en una investigación adicional encontramos que, en la primavera 3.2.4 de todos modos, el código que busca las anotaciones @ControllerAdvice también verifica 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 Ordered # LOWEST_PRECEDENCE, lo que significa que si tiene un controlador que debe tener la prioridad más baja, TODOS sus controladores deben tener un orden superior.
A continuación, se muestra un ejemplo que muestra cómo tener dos clases de controlador de excepciones con las anotaciones ControllerAdvice y Order que pueden brindar respuestas adecuadas cuando se produce una UserProfileException o 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() { .... } }
¡Disfrutar!
fuente
El orden de los manejadores de excepciones se puede cambiar mediante la
@Order
anotación.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
El valor de puede ser cualquier número entero.fuente
También encontré en la documentación 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-
Entonces, esto significa que si desea resolver este problema, deberá agregar su controlador de excepciones específico dentro del controlador que lanza esas excepciones. ANd para definir un único ControllerAdvice que maneja el manejador de excepciones predeterminado global.
Esto simplifica el proceso y no necesitamos la anotación de Pedido para manejar el problema.
fuente
Hay una situación similar en la excelente publicación " Manejo de excepciones en Spring MVC " en el blog de Spring, en la sección titulada Manejo de excepciones globales . Su escenario implica verificar las anotaciones ResponseStatus registradas en la clase de excepción y, si está presente, volver a lanzar la excepción para permitir que el marco las maneje. Es posible que pueda usar esta táctica general: intente determinar si hay un manejador más apropiado por ahí y volver a lanzar.
Alternativamente, hay algunas otras estrategias de manejo de excepciones cubiertas que podría considerar en su lugar.
fuente
Clase importante a manejar:
**@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); } }
Otras excepciones con prioridad baja
@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()); } }
fuente
también puede usar un valor numérico, como a continuación
@Order(value = 100)
fuente