携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情
我们数据返回的时候通常都有一个统一的数据格式,如果没有特殊处理的话,程序出现异常,那么返回的数据是另一种格式,这时我们会想到如果程序异常,我们也要求程序按照统一的数据格式进行返回,这就是统一异常处理。
统一异常处理有三种情况:
- 全局异常处理:不管你是什么异常,只要是异常都是用这个处理。
- 特定异常处理:针对不同异常使用不同处理。
- 自定义异常处理:自己写的异常,自己手动抛出。
1.全局异常处理
实现步骤:
- 创建异常处理类
- 在异常处理类上添加上@ControllerAdvice,底层原理其实是aop。
- 在异常处理类上编写具体针对异常处理方法,方法上面添加注解@ExceptionHandler。这个注解的作用是可以指定针对哪个异常处理,通常写成@ExceptionHandler(Exception.class),因为所有异常都继承Exception。
- 在编写的异常处理方法上还需要加上一个注解@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("执行全局异常处理");
}
}
实验结果:
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异常处理");
}
}
我们写上代码,在里面打印信息到控制台上。我们会发现,如果有全局处理异常和特定异常处理两个同时存在,那么只有特定异常会生效。结果如下图所示:
3.自定义异常处理
实现步骤:
- 创建自定义异常类,继承RuntimeException
- 在自定义异常类里面创建属性,这些属性可以有状态码或者相关信息
- 在全局异常处理类里面添加自定义异常处理类的方法,其实就是用@ExceptionHandler指定我们创建的自定义异常类
- 手动抛出自定义异常,你不手动抛出这个异常的话,它是不会生效的。
代码实现:
自定义异常类
@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("查询数据成功");
}
实验结果:
总结:全局异常统一处理有三种,即全局异常统一处理,特定异常处理和自定义异常处理。其中自定义异常处理应用较为频繁,我们可以在我们可能发生异常的代码出用try-catch进行处理,按照我提供的例子,我们还可以在手动抛出的时候自己指定状态码和异常信息。有同学可能会有以为,如果在项目里面用了自定义异常了,还需不需要用到全局异常处理,我个人觉得其实可以加上,作为一个兜底,以防别的异常没有被我们捕捉到。特定异常处理大家可以根据自己的需要决定是否需要使用。