统一异常处理

144 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

我们数据返回的时候通常都有一个统一的数据格式,如果没有特殊处理的话,程序出现异常,那么返回的数据是另一种格式,这时我们会想到如果程序异常,我们也要求程序按照统一的数据格式进行返回,这就是统一异常处理

统一异常处理有三种情况:

  1. 全局异常处理:不管你是什么异常,只要是异常都是用这个处理。
  2. 特定异常处理:针对不同异常使用不同处理。
  3. 自定义异常处理:自己写的异常,自己手动抛出。

1.全局异常处理

实现步骤:

  1. 创建异常处理类
  2. 在异常处理类上添加上@ControllerAdvice,底层原理其实是aop。
  3. 在异常处理类上编写具体针对异常处理方法,方法上面添加注解@ExceptionHandler。这个注解的作用是可以指定针对哪个异常处理,通常写成@ExceptionHandler(Exception.class),因为所有异常都继承Exception。
  4. 在编写的异常处理方法上还需要加上一个注解@ResponseBody,因为你要将异常数据返回给前端同学。

实现过程:

数据统一格式:

/统一返回结果类
@Data
public class Result<T> {

    private Integer code; //状态码

    private String message; //返回状态信息(成功 失败)

    private T data; //返回数据

    public Result() {}

    //成功的方法,有data数据
    public static <T> Result<T> ok(T data){
        Result<T> result = new Result<>();
        if (data != null){
            result.setData(data);
        }
        result.setCode(200);
        result.setMessage("成功");
        return result;
    }

    //失败的方法,有data数据
    public static <T> Result<T> fail(T data){
        Result<T> result = new Result<>();
        if (data != null){
            result.setData(data);
        }
        result.setCode(201);
        result.setMessage("失败");
        return result;
    }

    public Result<T> message(String msg){
        this.setMessage(msg);
        return this;
    }

    public Result<T> code(Integer code){
        this.setCode(code);
        return this;
    }

}

编写全局异常处理类:

@ControllerAdvice
public class GlobalExceptionHandler {

    //全局异常处理
    @ExceptionHandler(Exception.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail(null).message("执行全局异常处理");
    }

}

实验结果:

image.png

2.特定异常处理

看到了这里,有些同学可能已经猜到了特定异常应该怎么处理了。我们前面不是说过@ExceptionHandler的作用是可以指定针对哪个异常处理,比方说你想针对ArithmeticException进行异常处理,那么你可以在全局异常类里面定义一个方法,然后在方法上标注@ExceptionHandler(ArithmeticException.class)。 问题来了,全局异常处理类里面既有全局异常处理,又有针对特定异常的处理,那么那个先生效呢?还是只生效其中一个?

@ControllerAdvice
public class GlobalExceptionHandler {

    //全局异常处理
    @ExceptionHandler(Exception.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(Exception e){
        System.out.println("全局...");
        e.printStackTrace();
        return Result.fail(null).message("执行全局异常处理");
    }

    //特定异常处理ArithmeticException
    @ExceptionHandler(ArithmeticException.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(ArithmeticException e){
        System.out.println("特定...");
        e.printStackTrace();
        return Result.fail(null).message("执行ArithmeticException异常处理");
    }

}

我们写上代码,在里面打印信息到控制台上。我们会发现,如果有全局处理异常和特定异常处理两个同时存在,那么只有特定异常会生效。结果如下图所示:

image.png

image.png

3.自定义异常处理

实现步骤:

  1. 创建自定义异常类,继承RuntimeException
  2. 在自定义异常类里面创建属性,这些属性可以有状态码或者相关信息
  3. 在全局异常处理类里面添加自定义异常处理类的方法,其实就是用@ExceptionHandler指定我们创建的自定义异常类
  4. 手动抛出自定义异常,你不手动抛出这个异常的话,它是不会生效的。

代码实现:

自定义异常类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GgktException extends RuntimeException {
    private Integer code;
    private String msg;
}

在全局异常类里面指定我们的自定义异常类

@ControllerAdvice
public class GlobalExceptionHandler {

    //全局异常处理
    @ExceptionHandler(Exception.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(Exception e){
        System.out.println("全局...");
        e.printStackTrace();
        return Result.fail(null).message("执行全局异常处理");
    }

    //特定异常处理ArithmeticException
    @ExceptionHandler(ArithmeticException.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(ArithmeticException e){
        System.out.println("特定...");
        e.printStackTrace();
        return Result.fail(null).message("执行ArithmeticException异常处理");
    }

    //GgktException
    //特定异常处理ArithmeticException
    @ExceptionHandler(GgktException.class)//指定针对那个异常处理,所有异常继承Exception
    @ResponseBody
    public Result error(GgktException e){
        e.printStackTrace();
        //当手动抛出异常是,可以将设置的状态码和信息动态地添加进来
        return Result.fail(null).code(e.getCode()).message(e.getMsg());
    }

}

尝试着手动抛出我们的自定义异常类

@ApiOperation("查询所有讲师")
@GetMapping("findAll")
public Result findAllTeacher(){
    //模拟异常
    try {
        int a = 10/0;
    } catch (Exception e) {
        //抛出异常
        throw new GgktException(201,"执行自定义异常处理GgktException");
    }

    //调用service方法
    List<Teacher> list = teacherService.list();
    return Result.ok(list).message("查询数据成功");
}

实验结果:

image.png

总结:全局异常统一处理有三种,即全局异常统一处理,特定异常处理和自定义异常处理。其中自定义异常处理应用较为频繁,我们可以在我们可能发生异常的代码出用try-catch进行处理,按照我提供的例子,我们还可以在手动抛出的时候自己指定状态码和异常信息。有同学可能会有以为,如果在项目里面用了自定义异常了,还需不需要用到全局异常处理,我个人觉得其实可以加上,作为一个兜底,以防别的异常没有被我们捕捉到。特定异常处理大家可以根据自己的需要决定是否需要使用。