Java @仅当存在';没有异常的映射

Java @仅当存在';没有异常的映射,java,spring,spring-mvc,exception,exception-handling,Java,Spring,Spring Mvc,Exception,Exception Handling,使用spring-web-4.2.6,我有以下控制器和异常处理程序: @ControllerAdvice public class ExceptionsHandler { @ExceptionHandler(Exception.class) public ResponseEntity<ErrorDTO> HandleDefaultException(Exception ex) { ... } @ExceptionHandler(Interna

使用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("");
    }
}
@ControllerAdvice
公共类异常处理程序{
@ExceptionHandler(Exception.class)
公共响应处理的默认异常(异常ex){
...
}
@ExceptionHandler(InternalError.class)
公共响应HandleInternalError(InternalError ex){
...
}
}
@RestController
@请求映射(“/myController”)
公共类MyController{
@RequestMapping(value=“/myAction”,method=RequestMethod.POST)
公共布尔myAction(){
抛出新的内部错误(“”);
}
}
出于某种原因,将调用ExceptionHandler的HandleDefaultException(对于Exception.class)方法,而不是HandleInternalError调用类型为NestedServletException的异常

删除默认调用时,将使用正确的InternalError异常调用IntenalError调用

我不想删除默认调用,因为拥有一个默认处理程序对我来说很重要,这样可以为我的用户提供更好的体验

我错过了什么

编辑:


显然,我在使用spring-web-4.3.3,没有要求它。我不明白为什么,这是我的Gradle dependencies树:

Spring MVC应该只展示您在4.3及以上版本中描述的行为。看见以前,SpringMVC不会向
@ExceptionHandler
方法公开任何
可丢弃的
值。看


从4.3开始,Spring MVC将捕获从处理程序方法抛出的任何
可丢弃的
,并将其包装在
NestedServletException
中,然后将其公开给正常的
ExceptionHandlerExceptionResolver
过程

下面简要介绍一下它的工作原理:

  • 检查处理程序方法的
    @Controller
    类是否包含任何
    @ExceptionHandler
    方法
  • 如果是,则尝试解析一个可以处理
    异常
    类型的异常(包括
    NestedServletException
    )。如果可以,它会使用它(如果找到多个匹配项,则会进行排序)。如果不能,并且
    异常
    有一个异常,它将打开并再次尝试查找该异常的处理程序。原因现在可能是可丢弃的(或其任何子类型)
  • 如果没有。它获取所有的
    @ControllerAdvice
    类,并尝试在这些类中找到
    异常
    类型(包括
    NestedServletException
    )的处理程序。如果可以的话,它会使用它。如果不能,并且
    异常
    有一个
    原因
    ,它将打开该异常,然后使用该
    可丢弃类型重试
  • 在您的示例中,您的
    MyController
    抛出一个
    内部错误
    。由于这不是
    Exception
    的子类,SpringMVC将其包装在
    NestedServletException

    MyController
    没有任何
    @ExceptionHandler
    方法,因此Spring MVC跳过它。您有一个
    @ControllerAdvice
    注释类,
    exceptionhandler
    ,因此Spring MVC会对此进行检查。
    @ExceptionHandler
    注释的
    HandleDefaultException
    方法可以处理
    异常
    ,因此Spring MVC选择它来处理
    NestedServletException

    如果删除
    HandleDefaultException
    ,Spring MVC将找不到可以处理
    异常的内容。然后,它将尝试打开
    NestedServletException
    ,并检查其
    原因。然后,它将找到可以处理该内部错误的
    HandleInternalError

    这不是一个容易处理的问题。以下是一些选项:

    创建一个处理NestedServletException的
    @ExceptionHandler
    ,并自己检查
    内部错误

    @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
        }
    }
    
    现在Spring将首先尝试在
    MyController
    中找到
    NestedServletException
    的处理程序。它找不到任何异常,因此它将打开
    NestedServletException
    ,并获得一个
    InternalError
    。它将尝试查找
    InternalError
    的处理程序,并查找
    HandleInternalError


    这样做的缺点是,如果多个控制器的处理程序方法抛出
    InternalError
    ,则必须向每个控制器添加
    @ExceptionHandler
    。这也可能是一个优势。您的处理逻辑将更接近抛出错误的对象。

    您确定使用的是4.2.6吗?您描述的行为应该只适用于4.3+。显然,我使用的是spring-web-4.3.3,没有要求它。我不明白为什么,这是我的Gradle dependencies树:假设我使用4.3.3,我可以创建两个带有@ControllerAdvice的类,一个用于特定异常(如InternalError),一个用于默认异常,然后让带有特定异常的类先启动吗?我最终创建了两个带有@Order(1)的@ControllerAdvice类特殊例外情况和一般例外情况的顺序(2)。
    @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) {
             ...
        }
    }