使用 spring-web-4.2.6,我有以下 Controller 和 ExceptionHandler:
@ControllerAdvice
public class ExceptionsHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDTO> HandleDefaultException(Exception ex) {
...
}
@ExceptionHandler(InternalError.class)
public ResponseEntity<ErrorDTO> 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 调用。
删除默认调用时,IntenalError 调用会被调用并带有适当的 InternalError 异常。
我不想删除默认调用,因为拥有一个默认处理程序来为我的用户提供更好的体验对我来说很重要。
我在这里缺少什么?
编辑:
显然我正在使用 spring-web-4.3.3,而没有要求它。我不明白为什么,这是我的 Gradle 依赖树:http://pastebin.com/h6KXSyp2
最佳答案
Spring MVC 应该只展示您在 4.3 及更高版本中描述的行为。见 this JIRA issue .以前,Spring MVC 不会暴露任何 Throwable
值为 @ExceptionHandler
方法。看
从 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
.然后它会找到 HandleInternalError
哪个可以处理InternalError
.这不是一个容易处理的问题。以下是一些选项:
创建
@ExceptionHandler
处理 NestedServletException
并检查 InternalError
你自己。@ExceptionHandler(NestedServletException.class)
public ResponseEntity<String> 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
先说方法。只需移动 @ExceptionHandler
InternalError
的方法进入 Controller 。@RestController
@RequestMapping("/myController")
public class MyController {
@RequestMapping(value = "/myAction", method = RequestMethod.POST)
public boolean myAction() {
throw new InternalError("");
}
@ExceptionHandler(value = InternalError.class)
public ResponseEntity<String> HandleInternalError(InternalError ex) {
...
}
}
现在 Spring 将首先尝试为
NestedServletException
寻找一个处理程序。在 MyController
.它找不到任何东西,所以它会解开 NestedServletException
并得到一个 InternalError
.它将尝试为 InternalError
寻找处理程序并找到 HandleInternalError
.这有一个缺点,如果多个 Controller 的处理程序方法抛出
InternalError
,你必须添加一个 @ExceptionHandler
每个。这也可能是一个优势。您的处理逻辑将更接近引发错误的事情。
关于java - 仅当没有 Exception 映射时,才会调用 @ExceptionHandler for Error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40345227/