项目异常处理方案
基于昨天的基础框架,思考在连接前端时,数据传输格式应该以一个什么样的统一标准,使得后端的增删改查以一种相同的格式传给前端,前端统一接收。
设置统一返回结果类 用data来储存查询到的数据,code来储存成功或者失败的状态,用msg来给前端带去查询后的相关信息:
public class Result {
private Object data;
private Integer code;
private String msg;
}
开始基于昨天的框架增加数据分装类 Result,添加get,set方法,并且创建两个带参构造(一个为带消息的,一个为不带消息)和一个无参构造方法:
public class Result {
private Object data;
private Integer code;
private String msg;
public Result() {
}
public Result(Integer code,Object data,String msg ) {
this.data = data;
this.code = code;
this.msg = msg;
}
public Result( Integer code,Object data) {
this.data = data;
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
创建完成后再创建状态代码值 Code类,分别定义了增删改查的成功与失败的代码值:
public class Code {
public static final Integer SAVE_OK = 20011; //保存成功
public static final Integer DELETE_OK = 20021; //删除成功
public static final Integer UPDATE_OK = 20031; //更新成功
public static final Integer GET_OK = 20041; //查询成功
public static final Integer SAVE_ERR = 20010; //保存失败
public static final Integer DELETE_ERR = 20020; //删除失败
public static final Integer UPDATE_ERR = 20030; //更新失败
public static final Integer GET_ERR = 20040; //查询失败
}
将这些做好后,返回Contorller层,在之前定义的方法里,改变返回值为Result对象,并且在获得成功失败的时候用三元表达式来判断成功失败,将值传入Result对象并返回该对象。
eg:
@PostMapping
public Result save(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag? Code.SAVE_OK:Code.SAVE_ERR,flag);
}
异常处理器(上面我们已经解决了后端返回数据给前端的数据统一格式,但是当数据获取报错的时候,前端在接收时,就会接收到一大堆报错的代码,使得前端获取数据出错,我们要怎样解决报错后的数据传递呢?)
出现异常现象的常见位置与常见诱因如下(黑马视频内复制的):
- 框架内部抛出的异常:因使用不合规导效
- 数据层抛出的异常: 因外部服务器故障导致(例如: 服务器访问超时)
- 业务层抛出的异常: 因业务逻辑书写错误导致(例如: 遍历业务书写操作,导致索引异常等)
- 表现层抛出的异常:因数据收集、校验等规则导致(例如: 不匹配的数据类型间导致异常)
- 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
思考:报错会在不同的逻辑层抛出,我们应该怎么样来将报错进行统一处理,可以将报错都往上抛,统一到表现层(Controller层)进行处理并且使用AOP思想,这样就能处理不同的异常,而不用每个异常使用一次Try catch 大大减少了代码量使用,异常处理器(Spring MVC 已经帮我们想到了如何分类的使用AOP思想处理异常)来处理异常 创建ProjectExceptionAdvice.java 用来创建异常处理器 并定义doException来接收所有异常 :
@RestControllerAdvice //声明这个类是用来做异常处理的
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class) //异常处理标志(拦截异常)
public Result doException(Exception ex){
System.out.println("抓到你了..");
return new Result(66,null);
}
}
在Controller层中添加完异常处理器类后,测试异常是否成功被接收,在Controller层中的getById方法中添加一个除以0报错,在调用该方法看看控制台是否显示抓到你了..(显示抓到你了..就表明异常处理器成功处理异常):
以上的处理异常只是在Controller层出现的异常我们进行处理,但是其他层出现异常又该怎么办呢?学习黑马给的项目异常处理方案:
1.业务异常 (BusinessException):
- 发送对应消息传递给用户,提醒规范操作
2.系统异常 (SystemException)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
3.其他异常 (Exception)
- 发送固定消息传递给用户,安抚用户
- 发送特定消息给编程人员,提醒维护 (纳入预期范围内)
- 记录日志
在根目录在创建exception文件夹,在里面创建两个异常类,使得在出现该异常的时候i能够以统一的格式去接收数据。
BusinessException.java:
public class BusinessException extends RuntimeException{
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException( Integer code,String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
SystemException.java:
public class SystemException extends RuntimeException { //运行时异常
private Integer code;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException( Integer code,String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
我们根据不同异常可以将异常分为3大类写入ProjectExceptionAdvice.java内。
@RestControllerAdvice //声明这个类是用来做异常处理的
public class ProjectExceptionAdvice {
@ExceptionHandler(SystemException.class) //异常处理标志(拦截异常)
public Result doSystemException(SystemException ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员
return new Result(ex.getCode(),ex.getMessage());
}
@ExceptionHandler(BusinessException.class) //异常处理标志(拦截异常)
public Result doBusinessException(BusinessException ex){
return new Result(ex.getCode(),null,ex.getMessage());
}
@ExceptionHandler(Exception.class) //异常处理标志(拦截异常)
public Result doException(Exception ex){
//记录日志
//发送消息给运维
//发送邮件给开发人员
System.out.println("抓到你了..");
return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后重试!");
}
}
再在Code类中添加三个异常处理的代码:
public static final Integer SYSTEM_ERR = 50001; //系统错误
public static final Integer BUSINESS_ERR = 50002; //业务错误
public static final Integer SYSTEM_UNKNOW_ERR = 59999; //业务错误
测试:(我们在Service层的实现类中测试是否能够成功接收到异常并处理)在getById方法中人为的添加两个异常:
@Override
public Book getById(Integer id) {
if (id == -1){
throw new BusinessException(Code.BUSINESS_ERR,"输入的数据有误,请重新输入正确的数据");
}
//将可能出现的异常进行打包,转换成自定义异常
try{
int i = 1/0;
}catch (Exception e){
throw new SystemException(Code.SYSTEM_ERR,"服务器访问超时,请重试1!",e);
}
Book book = bookDao.getById(id);
return book;
}
在Postman中测试: