java - 仅当没有 Exception 映射时,才会调用 @ExceptionHandler for Error

标签 java spring spring-mvc exception exception-handling

使用 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方法。看

  • ExceptionHandler doesn't work with 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 .然后它会找到 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
        }
    }
    

    这很好,除非有一堆不同的 ErrorThrowable您要处理的类型。 (请注意,如果您不能或不知道如何处理它们,您可以重新抛出它们。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/

    相关文章:

    java - JSch:即使主机 key 指纹存在于 known_hosts 文件中,也会发生 UnknownHostKey 异常

    java - 无法启动 chrome,即在使用 Selenium 网格的节点上

    java - iText 中用于生成 PDF 的基本模板

    java - 如何从两个 MessageProducerSpec 创建 Spring Integration Flow?

    spring - 使用 Spring 3 验证的两种方法?

    spring - <表格:form have method GET or POST

    java - 如何在spring mvc @Controller中返回错误消息

    java - 在 stanford pos tagger 中编辑配置文件

    java - 如何使用 Spring MVC 从 API REST 获取对象

    java - 处理来自 @Service 类的 JSP 页面异常