Tsu*_*ury 3 java spring exception spring-mvc
使用 spring-web-4.2.6,我有以下控制器和异常处理程序:
@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("");
}
}
Run Code Online (Sandbox Code Playgroud)
由于某种原因,会调用 ExceptionsHandler 的 HandleDefaultException(对于 Exception.class)方法,而不是 HandleInternalError 调用,但异常类型为 NestedServletException。
删除默认调用时,将使用正确的 InternalError 异常调用 IntenalError 调用。
我不想删除默认调用,因为有一个默认处理程序可以为我的用户提供更好的体验,这对我来说很重要。
我在这里缺少什么?
编辑:
显然我正在使用 spring-web-4.3.3,而不需要它。我不明白为什么,这是我的 Gradle 依赖关系树: http: //pastebin.com/h6KXSyp2
Spring MVC 应该只表现出您在 4.3 及更高版本中描述的行为。请参阅此 JIRA 问题。以前,Spring MVC 不会Throwable向@ExceptionHandler方法公开任何值。看
从 4.3 开始,Spring MVC 将捕获Throwable处理程序方法中抛出的任何异常并将其包装在 a 中NestedServletException,然后将其公开给正常ExceptionHandlerExceptionResolver进程。
以下是其工作原理的简短描述:
@Controller类是否包含任何@ExceptionHandler方法。Exception类型(包括NestedServletException)的类型。如果可以,它会使用它(如果找到多个匹配项,则会进行一些排序)。如果它不能,并且Exception有一个cause,它会解包并再次尝试找到一个处理程序。cause现在可能是 a (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
}
}
Run Code Online (Sandbox Code Playgroud)
除非您想要处理一堆不同的Error或类型,否则这很好。Throwable(请注意,如果您不能或不知道如何处理它们,您可以重新抛出它们。Spring MVC 将默认为其他一些行为,可能返回 500 错误代码。)
或者,您可以利用 Spring MVC 首先检查@Controller( 或@RestController) 类中的@ExceptionHandler方法这一事实。@ExceptionHandler只需将方法移动InternalError到控制器中即可。
@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) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
NestedServletException现在 Spring 将首先尝试为in查找处理程序MyController。它找不到任何内容,因此它将解开NestedServletException并获得一个InternalError. 它将尝试找到InternalError和 find 的处理程序HandleInternalError。
这样做的缺点是,如果多个控制器的处理程序方法抛出异常InternalError,则必须@ExceptionHandler为每个控制器添加一个。这也可能是一个优势。您的处理逻辑将更接近引发错误的事物。