SpringBoot统一异常捕获

1,222 阅读4分钟

这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战

SpringBoot项目的接口服务中,我们在上一讲中定义了统一的返回结果,但是有些运行时产生的异常信息是不能够很好的预估到的,在返回结果数据之前就会报出异常,最终不能完成数据的返回。这种情况就会导致我们的统一结果出现了问题,因此针对接口中出现的异常信息,我们同样需要定义统一的规范来捕获程序运行过程中出现的异常。

至此,SpringBoot开发中三个基础且重要的统一规范就要完成了,之前两篇在这:

SpringBoot统一异常捕获

自定义统一异常类

Java程序中异常分为可查异常和不可查异常,其中可查异常要求必须进行捕获或者抛出;不可查异常又叫做运行时异常,则需要我们进行全局处理来维护接口服务的稳定和数据的正常返回。

在进行异常处理之前,需要定义一个全局的统一的异常类,来涵盖我们需要进行处理的异常类型,代码如下

@Data
public class MyException extends RuntimeException{
    private Integer code;

    public MyException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public MyException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getDesc());
        this.code = resultCodeEnum.getCode();
    }

    @Override
    public String toString() {
        return "MyException{" + "code=" + code + ", message=" + this.getMessage() + '}';
    }
}

统一异常处理器

@ControllerAdvice注解

@ControllerAdvice注解是一种作用于控制层的切面通知(Advice),该注解能够将通用的@ExceptionHandler、@InitBinder和@ModelAttributes方法收集到一个类型,并应用到所有控制器上。@ControllerAdvice注解是SpringBoot项目中统一异常处理的核心。

对于异常捕获的流程可分析为:

  1. 使用@ControllerAdvice注解标注异常处理器类
  2. 定义一个通用的异常捕获方法,并使用@ExceptionHandler注解标注异常处理器类中的方法来统一捕获指定的异常信息
  3. 自定义一个异常类,捕获针对项目或业务的异常;
  4. 异常的对象信息补充到统一结果枚举中;

统一异常处理器

定义一个异常处理类并使用@ControllerAdvice注解标注,标识当前类用来做异常的处理。 在异常处理类中分别定义了通用的异常处理方法、具体异常类型的处理方法以及自定义异常类的处理方法,SpringBoot中异常处理的流程是异常范围从小到达依次进行的。

@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 通用异常处理方法
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();;
        return Result.error();
    }

    /**
     * 指定具体异常处理方法
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public Result error(NullPointerException e){
        e.printStackTrace();
        return Result.setResult(ResultCodeEnum.NULL_POINT_ERROR);
    }

    /**
     * 返回自定义异常处理方法
     */
    @ExceptionHandler(MyException.class)
    @ResponseBody
    public Result error(MyException e){
        e.printStackTrace();
        return Result.error().code(e.getCode()).desc(e.getMessage());
    }
}

如代码所示,当异常是NullPointerException空指针异常时,会优先执行该异常类型为参数的方法并返回结果;自定义异常也是相同,只有当异常不属于这两类时,会调用通用异常处理方法来处理当前异常信息。

控制层捕获异常并返回

定义了全局异常类,并使用@ControllerAdvice注解定义了异常处理器以及相应异常处理方法后,便可以在controller控制层中定义相应的接口服务,并测试我们定义的全局异常是否发挥作用。 如下是自定义的接口信息,其内部调用了null,会产生一个NullPointerException

@RequestMapping(value = "/null")
public String nullException(){
    List<String> list = null;
    Integer count = list.size();
    return "success";
}

在对该接口进行请求后,返回结果为:

{"success":false,"code":20003,"desc":"空指针异常","data":null}

说明我们定义的异常处理器已经发生了作用。

最后

我们自定义的异常在服务接口中已经发挥作用,并且会根据异常的类型寻找对应方法来进行处理,最终把异常信息加入到统一的结果类中,作为服务接口的返回数据提供给调用者,这样我们就保证了服务接口的规范统一和结果稳定。