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

时间:2013-10-21 15:16:58

标签: spring-mvc exception-handling

我有使用@ControllerAdvice注释的多重类,每个类都有一个@ExceptionHandler方法。

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

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

这是人们期望Spring MVC的表现吗?我试图模仿Jersey的一个模式,它评估每个ExceptionMapper(等效组件)以确定它处理的声明类型与抛出的异常之间的距离,并始终使用最近的祖先。 / p>

7 个答案:

答案 0 :(得分:90)

  

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

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

默认情况下,Web MVC配置注册一个HandlerExceptionResolver bean,HandlerExceptionResolverComposite

  

委托给其他HandlerExceptionResolvers的列表。

那些其他解析器

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver
  4. 按该订单注册。出于这个问题的目的,我们只关心ExceptionHandlerExceptionResolver

      

    解决异常的AbstractHandlerMethodExceptionResolver   通过@ExceptionHandler方法。

    在上下文初始化时,Spring将为它检测到的每个ControllerAdviceBean注释类生成@ControllerAdviceExceptionHandlerExceptionResolver将从上下文中检索这些内容,并使用AnnotationAwareOrderComparator

    对其进行排序
      

    OrderComparator的扩展,支持Spring的Ordered   界面以及@Order@Priority注释,带有   由静态覆盖静态的Ordered实例提供的订单值   定义了注释值(如果有的话)。

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

    发生异常时,ExceptionHandlerExceptionResolver将遍历这些ExceptionHandlerMethodResolver并使用可以处理异常的第一个。{/ p>

    所以重点在于:如果@ControllerAdvice @ExceptionHandler Exception @ControllerAdvice@ExceptionHandler之前注册IOException一个更具体的例外,如@ControllerAdvice,第一个将被调用。如前所述,您可以通过{{1}}带注释的类实现Ordered或使用@Order@Priority注释并为其提供适当的值来控制该注册订单。

答案 1 :(得分:74)

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

享受!

答案 2 :(得分:17)

可以使用@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 {

    //...

}

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

答案 3 :(得分:3)

我还在文档中发现:

  

ExceptionHandlerMethod

     

受保护的ServletInvocableHandlerMethod   getExceptionHandlerMethod(HandlerMethod handlerMethod,                                例外情况)

     

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

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

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

答案 4 :(得分:2)

春季博客上的优秀“Exception Handling in Spring MVC”帖子中标题为Global Exception Handling的章节中也存在类似的情况。他们的场景涉及检查在异常类上注册的ResponseStatus注释,如果存在,则重新抛出异常以让框架处理它们。您可以使用这种一般策略 - 尝试确定是否有可能是更合适的处理程序并重新抛出。

或者,您可以考虑使用其他一些异常处理策略。

答案 5 :(得分:1)

要处理的重要类:

set(Boost_USE_STATIC_LIBS OFF) 
set(Boost_USE_MULTITHREADED ON)  
set(Boost_USE_STATIC_RUNTIME OFF) 
find_package(Boost 1.45.0 COMPONENTS *boost libraries here*) 

if(Boost_FOUND)
    include_directories(${Boost_INCLUDE_DIRS}) 
    add_executable(progname file1.cxx file2.cxx) 
    target_link_libraries(progname ${Boost_LIBRARIES})
endif()

其他低优先级的异常

**@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);
    }
}

答案 6 :(得分:0)

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

@Order(value = 100)
  

值越低优先级越高。默认值为* {@code   Ordered.LOWEST_PRECEDENCE},表示最低优先级(输给任何   其他*指定的订单值)