我有使用@ControllerAdvice
注释的多重类,每个类都有一个@ExceptionHandler
方法。
一个处理Exception
,意图是如果找不到更具体的处理程序,则应该使用它。
可悲的是,Spring MVC似乎总是使用最通用的案例(Exception
)而不是更具体的案例(例如IOException
)。
这是人们期望Spring MVC的表现吗?我试图模仿Jersey的一个模式,它评估每个ExceptionMapper
(等效组件)以确定它处理的声明类型与抛出的异常之间的距离,并始终使用最近的祖先。 / p>
答案 0 :(得分:90)
这是人们期望Spring MVC的表现吗?
从Spring 4.3.7开始,这里是Spring MVC的行为:它使用HandlerExceptionResolver
个实例来处理处理程序方法抛出的异常。
默认情况下,Web MVC配置注册一个HandlerExceptionResolver
bean,HandlerExceptionResolverComposite
,
委托给其他
HandlerExceptionResolvers
的列表。
那些其他解析器
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
按该订单注册。出于这个问题的目的,我们只关心ExceptionHandlerExceptionResolver
。
解决异常的
AbstractHandlerMethodExceptionResolver
通过@ExceptionHandler
方法。
在上下文初始化时,Spring将为它检测到的每个ControllerAdviceBean
注释类生成@ControllerAdvice
。 ExceptionHandlerExceptionResolver
将从上下文中检索这些内容,并使用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() {
....
}
}
享受!
答案 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},表示最低优先级(输给任何 其他*指定的订单值)