仅当没有Exception的映射时,才会调用@ExceptionHandler for Error
使用spring-web-4.2.6,我有以下Controller和ExceptionHandler:
@ControllerAdvice public class ExceptionsHandler { @ExceptionHandler(Exception.class) public ResponseEntity HandleDefaultException(Exception ex) { ... } @ExceptionHandler(InternalError.class) public ResponseEntity HandleInternalError(InternalError ex) { ... } } @RestController @RequestMapping("/myController") public class MyController { @RequestMapping(value = "/myAction", method = RequestMethod.POST) public boolean myAction() { throw new InternalError(""); } }
出于某种原因,调用ExceptionsHandler的HandleDefaultException(对于Exception.class)方法,但类型为NestedServletException,而不是HandleInternalError调用。
删除默认调用时,将使用正确的InternalErrorexception调用IntenalError调用。
我不想删除默认调用,因为对我来说,拥有一个默认处理程序以便为我的用户提供更好的体验非常重要。
我在这里想念的是什么?
编辑:
显然我正在使用spring-web-4.3.3,而不是要求它。 我不明白为什么,这是我的Gradle依赖树: http : //pastebin.com/h6KXSyp2
Spring MVC应该只展示您在4.3及更高版本中描述的行为。 请参阅此JIRA问题 。 以前,Spring MVC不会将任何Throwable
值暴露给@ExceptionHandler
方法。 看到
- ExceptionHandler不适用于Throwable
从4.3开始,Spring MVC将捕获从处理程序方法抛出的任何Throwable
并将其包装在NestedServletException
,然后它将暴露给正常的ExceptionHandlerExceptionResolver
进程。
以下是它如何工作的简短描述:
- 检查处理程序方法的
@Controller
类是否包含任何@ExceptionHandler
方法。 - 如果是,请尝试解析可以处理
Exception
类型的一个(包括NestedServletException
)。 如果可以,它会使用它(如果找到多个匹配,则会有一些排序)。 如果它不能,并且Exception
有cause
,它会解包并再次尝试为其找到处理程序。 这个cause
现在可能是Throwable
(或其任何子类型)。 - 如果没有。 它获取所有
@ControllerAdvice
类,并尝试在其中找到Exception
类型(包括NestedServletException
)的处理程序。 如果可以,它会使用它。 如果它不能,并且Exception
有cause
,它会解开它并再次尝试使用Throwable
类型。
在您的示例中, MyController
会抛出InternalError
。 由于这不是Exception
的子类,因此Spring MVC将其包装在NestedServletException
。
MyController
没有任何@ExceptionHandler
方法,因此Spring MVC会跳过它。 你有一个@ControllerAdvice
注释类, ExceptionsHandler
,所以Spring MVC会检查它。 @ExceptionHandler
注释的HandleDefaultException
方法可以处理Exception
,因此Spring MVC选择它来处理NestedServletException
。
如果删除HandleDefaultException
,Spring MVC将找不到可以处理Exception
东西。 然后它将尝试解NestedServletException
并检查其cause
。 然后它会找到可以处理该InternalError
的HandleInternalError
。
这不是一个容易处理的问题。 以下是一些选项:
创建一个处理NestedServletException
的@ExceptionHandler
,并自己检查InternalError
。
@ExceptionHandler(NestedServletException.class) public ResponseEntity HandleNested(NestedServletException ex) { Throwable cause = ex.getCause(); if (cause instanceof InternalError) { // deal with it } else if (cause instanceof OtherError) { // deal in some other way } }
这很好,除非你想要处理一堆不同的Error
或Throwable
类型。 (请注意,如果您不能或不知道如何处理它们,您可以重新抛出它们.Spring MVC将默认为其他一些行为,可能会返回500错误代码。)
或者,您可以利用Spring MVC首先检查@Controller
(或@RestController
)类的@ExceptionHandler
方法。 只需将InternalError
的@ExceptionHandler
方法移动到控制器中即可。
@RestController @RequestMapping("/myController") public class MyController { @RequestMapping(value = "/myAction", method = RequestMethod.POST) public boolean myAction() { throw new InternalError(""); } @ExceptionHandler(value = InternalError.class) public ResponseEntity HandleInternalError(InternalError ex) { ... } }
现在Spring将首先尝试在MyController
为NestedServletException
找到一个处理程序。 它将找不到任何内容,因此它将解NestedServletException
并获取InternalError
。 它将尝试为InternalError
找到一个处理程序并找到HandleInternalError
。
这样做的缺点是,如果多个控制器的处理程序方法抛出InternalError
,则必须向每个控制器添加一个@ExceptionHandler
。 这也可能是一个优势。 您的处理逻辑将更接近抛出错误的事物。