设置多个@ControllerAdvice @ExceptionHandlers的优先级

Eng*_*_DJ 68 exception-handling spring-mvc

我有多个用类注释的类@ControllerAdvice,每个类都带有一个@ExceptionHandler方法.

一个处理Exception的目的是如果找不到更具体的处理程序,则应该使用它.

可悲的是,Spring MVC似乎总是使用最通用的case(Exception)而不是更具体的case(IOException例如).

这是人们期望Spring MVC的表现吗?我正在尝试模拟来自Jersey的模式,它评估每个ExceptionMapper(等效组件)以确定它处理的声明类型与已抛出的异常的距离,并始终使用最近的祖先.

Sot*_*lis 104

这是人们期望Spring MVC的表现吗?

从Spring 4.3.7开始,这里是Spring MVC的行为:它使用HandlerExceptionResolver实例来处理由处理程序方法抛出的异常.

默认情况下,Web MVC配置注册一个HandlerExceptionResolverbean HandlerExceptionResolverComposite,即

委托给其他人的名单HandlerExceptionResolvers.

那些其他的解析器是

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver

在那个顺序登记.出于这个问题的目的,我们只关心ExceptionHandlerExceptionResolver.

AbstractHandlerMethodExceptionResolver,通过解析异常@ExceptionHandler的方法.

在上下文初始化时,Spring将为它检测到的ControllerAdviceBean每个带@ControllerAdvice注释的类生成一个.该ExceptionHandlerExceptionResolver会从上下文检索这些,并使用排序,使用AnnotationAwareOrderComparator

是的扩展OrderComparator支持Spring的Ordered 界面,以及在@Order@Priority注解,与由有序实例覆盖静态定义的注释值(如果有的话)提供的命令值.

然后,它将ExceptionHandlerMethodResolver为每个ControllerAdviceBean实例注册一个(将可用@ExceptionHandler方法映射到它们要处理的异常类型).这些最终以相同的顺序添加到LinkedHashMap(保留迭代顺序).

当发生异常时,ExceptionHandlerExceptionResolver将遍历这些ExceptionHandlerMethodResolver并使用可以处理异常的第一个异常.

因此,这里的一点是:如果你有一个@ControllerAdvice带有@ExceptionHandler用于Exception该被另一注册前@ControllerAdvice与类@ExceptionHandler的更具体的例外,比如IOException,是第一个将被调用.正如前面提到的,你可以有你的控制是为了注册@ControllerAdvice注解类实现Ordered或注释它@Order或者@Priority,给它一个合适的值.

  • 此外,如果在@ControllerAdvice中有多个@ExceptionHandler方法,则选择一个处理抛出的异常的最特定超类的方法。 (2认同)

Dom*_*ton 82

Sotirios Delimanolis对他的回答非常有帮助,经过进一步的调查,我们发现,在3.2.4版本中,查找@ControllerAdvice注释的代码也会检查@Order注释的存在并对ControllerAdviceBeans列表进行排序.

没有@Order注释的所有控制器的最终默认顺序是Ordered#LOWEST_PRECEDENCE,这意味着如果您有一个控制器需要具有最低优先级,那么所有控制器都需要具有更高的顺序.

下面是一个示例,说明如何使用ControllerAdvice和Order注释创建两个异常处理程序类,以便在发生UserProfileException或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() {
        ....
    }
}
Run Code Online (Sandbox Code Playgroud)
  • 请参阅ControllerAdviceBean#initOrderFromBeanType()
  • 请参阅ControllerAdviceBean#findAnnotatedBeans()
  • 请参见ExceptionHandlerExceptionResolver #initExceptionHandlerAdviceCache()

请享用!


Kor*_*tor 19

可以使用@Order注释更改异常处理程序的顺序.

例如:

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 {

    //...

}
Run Code Online (Sandbox Code Playgroud)

@Order的值可以是任何整数.


小智 6

我还在文档中发现:

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-

异常处理方法

受保护的 ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception 异常)

查找给定异常的 @ExceptionHandler 方法。默认实现首先在控制器的类层次结构中搜索方法,如果没有找到,它会继续搜索其他 @ExceptionHandler 方法,假设检测到一些 @ControllerAdvice Spring 管理的 bean。参数: handlerMethod - 引发异常的方法(可能为 null) exception - 引发的异常返回:处理异常的方法,或 null

所以这意味着如果你想解决这个问题,你需要在抛出这些异常的控制器中添加你的特定异常处理程序。并定义一个且唯一的 ControllerAdvice 处理全局默认异常处理程序。

这简化了过程,我们不需要 Order 注释来处理问题。


Vic*_*cky 5

您还可以使用数值,如下所示

@Order(value = 100)
Run Code Online (Sandbox Code Playgroud)

较低的值具有较高的优先级。默认值是 * {@code Ordered.LOWEST_PRECEDENCE},表示最低优先级(输给任何其他指定的顺序值)