Spring MVC 设置多个@ControllerAdvice @异常处理程序的优先级

jhkqcmku  于 2022-11-14  发布在  Spring
关注(0)|答案(8)|浏览(530)

我有多个用@ControllerAdvice注解的类,每个类都有一个@ExceptionHandler方法。
处理Exception的目的是,如果没有找到更具体的处理程序,则应该使用它。
遗憾的是,Spring MVC似乎总是使用最通用的情况(Exception),而不是更具体的情况(例如IOException)。
这就是Spring MVC的行为吗?我尝试模拟Jersey的模式,它评估每个ExceptionMapper(等效组件),以确定它处理的声明类型与抛出的异常有多远,并且总是使用最近的祖先。

ohfgkhjo

ohfgkhjo1#

这就是人们所期望的SpringMVC的行为吗?
从Spring 4.3.7开始,Spring MVC的行为如下:它使用HandlerExceptionResolver示例来处理由处理程序方法抛出的异常。
默认情况下,Web MVC配置注册一个HandlerExceptionResolver bean,即HandlerExceptionResolverComposite,它
委派给其他HandlerExceptionResolvers的清单。
其他解析器是

  1. ExceptionHandlerExceptionResolver
  2. ResponseStatusExceptionResolver
  3. DefaultHandlerExceptionResolver
    在此问题中,我们只关心ExceptionHandlerExceptionResolver
    透过@ExceptionHandler方法解析例外状况的AbstractHandlerMethodExceptionResolver
    在上下文初始化时,Spring将为它检测到的每个带@ControllerAdvice注解的类生成一个ControllerAdviceBean
    OrderComparator的扩展,支持Spring的Ordered接口以及@Order@Priority注解,Ordered示例提供的顺序值将覆盖静态定义的注解值(如果有)。
    然后为每个ControllerAdviceBean示例注册一个ExceptionHandlerMethodResolver(将可用的@ExceptionHandler方法Map到它们要处理的异常类型),最后以相同的顺序添加到LinkedHashMap(保持迭代顺序)。
    当异常发生时,ExceptionHandlerExceptionResolver将遍历这些ExceptionHandlerMethodResolver,并使用第一个可以处理异常的ExceptionHandlerMethodResolver
    所以这里的重点是:如果您有一个@ControllerAdvice,它带有Exception@ExceptionHandler,它在另一个@ControllerAdvice类之前注册,这个@ControllerAdvice类带有@ExceptionHandler,用于更具体的异常,比如IOException,那么第一个类将被调用。如前所述,您可以通过让您@ControllerAdvice注解类实现Ordered,或者用@Order@Priority对其进行注解并赋予适当的值来控制注册顺序。
gfttwv5a

gfttwv5a2#

Sotirios Delimanolis的回答非常有帮助,在进一步的研究中我们发现,无论如何,在3.2.4 Spring 版中,查找@ControllerAdvice注解的代码也会检查@Order注解的存在,并对ControllerAdviceBean列表进行排序。
所有不带@Order注解的控制器的默认顺序为Ordered#LOWEST_PRECEDENCE,这意味着如果您有一个控制器需要具有最低优先级,则所有控制器都需要具有较高的顺序。
下面的示例显示了如何让两个带有ControllerAdvice和Order注解的异常处理程序类在发生UserProfileException或RuntimeException时提供适当的响应。

class UserProfileException extends RuntimeException {
}

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
class UserProfileExceptionHandler {
    @ExceptionHandler(UserProfileException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleUserProfileException() {
        ....
    }
}

@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
class DefaultExceptionHandler {

    @ExceptionHandler(RuntimeException)
    @ResponseBody
    ResponseEntity<ErrorResponse> handleRuntimeException() {
        ....
    }
}
  • 请参阅控制器建议Bean #initOrderFromBean类型()
  • 请参阅控制器建议Bean #findAnnotatedBeans()
  • 请参见异常处理程序异常解决程序#initExceptionHandlerAdviceCache()

好好享受吧!

im9ewurl

im9ewurl3#

可以使用@Order注解来变更异常处理常式的顺序。
例如:

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ControllerAdvice;

@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CustomExceptionHandler {

    //...

}

@Order的值可以是任何整数。

jm2pwxwz

jm2pwxwz4#

我还在文档中发现:
https://docs.spring.io/spring-framework/docs/4.3.4.RELEASE/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.html#getExceptionHandlerMethod-org.springframework.web.method.HandlerMethod-java.lang.Exception-
异常处理程序方法
如果您有任何异常,请使用以下方法:
查找给定异常的@ExceptionHandler方法。默认实现首先在控制器的类层次结构中搜索方法,如果未找到,它将继续搜索其他@ExceptionHandler方法(假定检测到某些@ControllerAdvice Spring托管的Bean)。参数:handlerMethod -引发异常的方法(可能为空)exception -引发的异常返回:用于处理异常的方法,或者为null
因此这意味着如果你想解决这个问题,你将需要在抛出那些异常的控制器内添加你特定的异常处理程序. ANd来定义一个且唯一的ControllerAdvice处理全局默认异常处理程序。
这简化了过程,我们不需要Order注解来处理问题。

a2mppw5e

a2mppw5e5#

在Spring博客上的“Exception Handling in Spring MVC“文章中,在标题为”全局异常处理“的一节中,有一个类似的情况,他们的场景涉及检查异常类上注册的ResponseStatus注解,如果存在,重新抛出异常,让框架来处理它们。尝试确定是否存在更合适的处理程序并重新引发。
或者,您可以参考一些其他的异常处理策略。

ql3eal8s

ql3eal8s6#

也可以使用数字值,如下所示

@Order(value = 100)

值越低,优先级越高。默认值为 * {@code Ordered.LOWEST_PRECEDENCE},表示优先级最低(低于任何其他 * 指定的顺序值)

xqk2d5yq

xqk2d5yq7#

要处理的重要类:

**@Order(Ordered.HIGHEST_PRECEDENCE)**
public class FunctionalResponseEntityExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(FunctionalResponseEntityExceptionHandler.class);

    @ExceptionHandler(EntityNotFoundException.class)
    public final ResponseEntity<Object> handleFunctionalExceptions(EntityNotFoundException ex, WebRequest request)
    {
        logger.error(ex.getMessage() + " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.getMessage(),
                request.getDescription(false),HttpStatus.NOT_FOUND.toString());
        return new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
    }
}

具有低优先级的其他例外

@ControllerAdvice
    public class GlobalResponseEntityExceptionHandler extends ResponseEntityExceptionHandler
    {
    private final Logger logger = LoggerFactory.getLogger(GlobalResponseEntityExceptionHandler.class);
    @ExceptionHandler(Exception.class)
    public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request)
    {
        logger.error(ex.getMessage()+ " " + ex);
        ExceptionResponse exceptionResponse= new ExceptionResponse(new Date(), ex.toString(),
                request.getDescription(false),HttpStatus.INTERNAL_SERVER_ERROR.toString());
    }
    }
fykwrbwg

fykwrbwg8#

如果你想分开你的异常处理程序(像我一样),你可以使用@Import来完成。
第一个

相关问题