Springboot多个异常处理类catch顺序

Springboot多个异常处理类catch顺序

通常情况下,我们都会定义一个全局异常处理类来处理异常,但是当我们定义了多个异常处理类,同时他们又存在父子继承关系的情况下,我们该怎么保证子类优先判断异常是否是自己可以捕获的呢?
如果父类先捕获该异常,那么该异常可能本该被子类处理掉,结果被父类的逻辑处理了,这肯定不是我们想要的。
所以springboot中的多个异常处理逻辑该怎么实现呢?

准备工作

1,引入springboot的springmvc整合包
2,创建两个异常类,这里面我们使用ConstraintViolationException参数检验异常类和Exception统一异常类两个。ConstraintViolationException是Exception的子类。

@ControllerAdvice
public class ArgumentsValidateAdvice {
   @ResponseBody
   @ExceptionHandler(value = ConstraintViolationException.class) //参数校验异常
   public String ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
      Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
      Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
      if (iterator.hasNext()) {
         ConstraintViolation<?> cvl = iterator.next();
         return ResponseUtil.appFail(cvl.getMessage());
      }
      return ResponseUtil.appFail("参数异常");
   }
}
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @ExceptionHandler(Exception.class)  //表示catch什么异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public String exception(Exception e, HttpServletResponse response) {
        logger.error("global exception msg : {}", e.getMessage(), e);
        return ResponseUtil.appFail(response, "服务器异常!");
    }
}

3, 随便写一个SpringMvc的controller并提供一个外部请求的方法,我们只需要在方法的入参上面添加@NotNull @NotEmpty等参数检验注解即可
注:没使用过Spring mvc的参数校验的同学可以使用其他的异常或者自定义异常,原理都一样,只要是继承Exception的异常就好了,主要是保证多个异常间存在父子关系就好。

4,启动springboot,访问接口,然后我们估计不带参数,这时候参数校验就会抛出异常。
这时候我们是希望ArgumentsValidateAdvice 捕获到我们的参数异常,并返回给前端该异常信息的。如果是GlobalExceptionHandler先获取到了异常,那么他就会放回给前端“服务器异常!”,这显然不是我们想要的。
所以我们要保证ArgumentsValidateAdvice在异常捕获过程中要先于GlobalExceptionHandler。

一般都会想到实现Ordered接口或者点解@Order注解设置bean的排序优先级。那么这样真的可以吗,让我们debug看一下

在异常处理类的异常处理方法上打上断点

当SpringMvc发生异常的时候,会调用org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#doResolveHandlerMethodException.
在这里插入图片描述
这个方法会从exceptionHandlerAdviceCache中获取合适的异常处理类来处理发生的异常,所以我们下面应该继续找exceptionHandlerAdviceCache中的元素是怎么放进去的

我们定位到了org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache方法
在这里插入图片描述
可以看到是这里往exceptionHandlerAdviceCache中插入的元素。
所以我们需要在往exceptionHandlerAdviceCache插入元素的时候,让ArgumentsValidateAdvice 能在GlobalExceptionHandler之前放入。这样发生异常的时候ArgumentsValidateAdvice 就能被先拿出来考虑是不是符合捕获要求

上面的方法先通过org.springframework.web.method.ControllerAdviceBean#findAnnotatedBeans找到所有的ControllerAdvice注解的bean。
然后重点来了,就是AnnotationAwareOrderComparator.sort(adviceBeans);
点进去看到,这个排序是根据org.springframework.core.OrderComparator#compare来排
序的。
在这里插入图片描述
可以看到先根据是否继承PriorityOrdered来排,如果没有在调用getOrder()方法来排序
在这里插入图片描述
也就是根据是否是Ordered实例,如果是的话取Ordered的值来排序。
那么我们就需要让我们的advice是Order的实例。
方法调用到这里,我们继续跟踪得到上面图中的(Ordered) obj.getOrder()方法最终调用的是:
org.springframework.web.method.ControllerAdviceBean#getOrder

在这里插入图片描述
也就是说我们的advice被包装成了ControllerAdviceBean对象

所以我们需要看ControllerAdviceBean的order是哪里来的。
在这里插入图片描述
我们发现是在ControllerAdviceBean的构造方法中通过org.springframework.web.method.ControllerAdviceBean#initOrderFromBeanType对order进行的赋值。
在这里插入图片描述
继续看org.springframework.core.annotation.OrderUtils#getOrder(java.lang.Class<?>, java.lang.Integer)
在这里插入图片描述

注意,这个参数type就是我们的ArgumentsValidateAdvice等异常捕获advice
我们发现,如果我们的advice有Order注解,他就会取注解里面的值来设置order,如果没有该注解,他就会使用默认值,也就是Ordered.LOWEST_PRECEDENCE。

综上:

异常捕获类的排序只需要添加@Order注解,并指定order的值即可。
所以最后我们在ArgumentsValidateAdvice上添加@Order(Ordered.LOWEST_PRECEDENCE - 2),只要保证比GlobalExceptionHandler 的order靠前即可。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值