首页 > 其他分享 >8. 统一异常处理

8. 统一异常处理

时间:2023-07-03 09:00:21浏览次数:35  
标签:code 自定义 处理 处理器 Integer 异常 public 统一

1. 问题描述

在讲解这一部分知识点之前,我们先来演示个效果,修改 BookController 类的getById​ 方法

@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
    //手动添加一个错误信息
    if(id==1){
        int i = 1/0;
    }
    Book book = bookService.getById(id);
    Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
    String msg = book != null ? "" : "数据查询失败,请重试!";
    return new Result(code,book,msg);
}

重新启动运行项目,使用 PostMan 发送请求,当传入的 id 为 1,则会出现如下效果:

image

前端接收到这个信息后和之前我们约定的格式不一致,这个问题该如何解决?

在解决问题之前,我们先来看下异常的种类及出现异常的原因:

  • 框架内部抛出的异常:因使用不合规导致
  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)

看完上面这些出现异常的位置,你会发现,在我们开发的任何一个位置都有可能出现异常,而且这些异常是不能避免的。所以我们就得将异常进行处理。

思考

  1. 各个层级均出现异常,异常处理代码书写在哪一层?
    所有的异常均抛出到表现层进行处理
  2. 异常的种类很多,表现层如何将所有的异常都处理到呢?
    异常分类
  3. 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决?
    AOP

对于上面这些问题及解决方案,SpringMVC 已经为我们提供了一套解决方案:

  • 异常处理器:

    • 集中的、统一的处理项目中出现的异常。

image

2. 异常处理器的使用

2.1 使用步骤

步骤 1:创建异常处理器类

//@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
    //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public void doException(Exception ex){
      	System.out.println("嘿嘿,异常你哪里跑!")
    }
}

确保 SpringMvcConfig 能够扫描到异常处理器类

步骤 2:让程序抛出异常

修改BookController​ 的 getById 方法,添加int i = 1/0​.

@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
  	int i = 1/0;
    Book book = bookService.getById(id);
    Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
    String msg = book != null ? "" : "数据查询失败,请重试!";
    return new Result(code,book,msg);
}

步骤 3:运行程序,测试

image

异常处理器类返回结果给前端

//@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
    //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
      	System.out.println("嘿嘿,异常你哪里跑!")
        return new Result(666,null,"嘿嘿,异常你哪里跑!");
    }
}

启动运行程序,测试

image

知识点 1:@RestControllerAdvice

名称 @RestControllerAdvice
类型 类注解
位置 Rest 风格开发的控制器增强类定义上方
作用 为 Rest 风格开发的控制器类做增强

说明:此注解自带@ResponseBody 注解与@Component 注解,具备对应的功能

image

知识点 2:@ExceptionHandler

名称 @ExceptionHandler
类型 方法注解
位置 专用于异常处理的控制器方法上方
作用 设置指定异常的处理方案,功能等同于控制器方法,
出现异常后终止原始控制器执行,并转入当前方法执行

说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常

3. 项目异常处理方案

3.1 异常分类

异常处理器我们已经能够使用了,那么在咱们的项目中该如何来处理异常呢?

因为异常的种类有很多,如果每一个异常都对应一个@ExceptionHandler,那得写多少个方法来处理各自的异常,所以我们在处理异常之前,需要对异常进行一个分类:

  • 业务异常(BusinessException)

    • 规范的用户行为产生的异常

      • 用户在页面输入内容的时候未按照指定格式进行数据填写,如在年龄框输入的是字符串

image

  • 不规范的用户行为操作产生的异常

    • 如用户故意传递错误数据

      image

  • 系统异常(SystemException)

    • 项目运行过程中可预计但无法避免的异常

      • 比如数据库或服务器宕机
  • 其他异常(Exception)

    • 编程人员未预期到的异常,如:用到的文件不存在

image

将异常分类以后,针对不同类型的异常,要提供具体的解决方案:

3.2 异常解决方案

  • 业务异常(BusinessException)

    • 发送对应消息传递给用户,提醒规范操作

      • 大家常见的就是提示用户名已存在或密码格式不正确等
  • 系统异常(SystemException)

    • 发送固定消息传递给用户,安抚用户

      • 系统繁忙,请稍后再试
      • 系统正在维护升级,请稍后再试
      • 系统出问题,请联系系统管理员等
    • 发送特定消息给运维人员,提醒维护

      • 可以发送短信、邮箱或者是公司内部通信软件
    • 记录日志

      • 发消息和记录日志对用户来说是不可见的,属于后台程序
  • 其他异常(Exception)

    • 发送固定消息传递给用户,安抚用户

    • 发送特定消息给编程人员,提醒维护(纳入预期范围内)

      • 一般是程序没有考虑全,比如未做非空校验等
    • 记录日志

3.3 异常解决方案的具体实现

思路:

1.先通过自定义异常,完成 BusinessException 和 SystemException 的定义

2.将其他异常包装成自定义异常类型

3.在异常处理器类中对不同的异常进行处理

步骤 1:自定义异常类

//自定义异常处理器,用于封装异常信息,对异常进行分类
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;
    }

}

//自定义异常处理器,用于封装异常信息,对异常进行分类
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;
    }

}


说明:

  • 让自定义异常类继承RuntimeException​ 的好处是,后期在抛出这两个异常的时候,就不用在 try...catch...或 throws 了
  • 自定义异常类中添加code​ 属性的原因是为了更好的区分异常是来自哪个业务的

步骤 2:将其他异常包成自定义异常

假如在 BookServiceImpl 的 getById 方法抛异常了,该如何来包装呢?

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_TIMEOUT_ERR,"服务器访问超时,请重试!",e);
    }
    return bookDao.getById(id);
}

具体的包装方式有:

  • 方式一:try{}catch(){}​ 在 catch 中重新 throw 我们自定义异常即可。
  • 方式二:直接 throw 自定义异常即可

上面为了使code​ 看着更专业些,我们在 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;
    public static final Integer SYSTEM_ERR = 50001;
    public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
    public static final Integer SYSTEM_UNKNOW_ERR = 59999;

    public static final Integer BUSINESS_ERR = 60002;
}

步骤 3:处理器类中处理自定义异常

//@RestControllerAdvice用于标识当前类为REST风格对应的异常处理器
@RestControllerAdvice
public class ProjectExceptionAdvice {
    //@ExceptionHandler用于设置当前处理器类对应的异常类型
    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException ex){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    @ExceptionHandler(BusinessException.class)
    public Result doBusinessException(BusinessException ex){
        return new Result(ex.getCode(),null,ex.getMessage());
    }

    //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
    }
}

步骤 4:运行程序

根据 ID 查询,

如果传入的参数为 1,会报BusinessException

image

对于异常我们就已经处理完成了,不管后台哪一层抛出异常,都会以我们与前端约定好的方式进行返回,前端只需要把信息获取到,根据返回的正确与否来展示不同的内容即可。

小结

以后项目中的异常处理方式为:

image

标签:code,自定义,处理,处理器,Integer,异常,public,统一
From: https://www.cnblogs.com/NorthPoet/p/17521864.html

相关文章

  • x64 架构,也称作AMD64或Intel 64,是指一种64位的处理器架构,是对x86架构的扩展和升级。x6
    x64架构,也称作AMD64或Intel64,是指一种64位的处理器架构,是对x86架构的扩展和升级。x64架构支持更大的内存寻址范围和更高的性能,适用于运行64位操作系统和应用程序。x64架构最早由AMD引入,并在2003年取得了广泛的市场认可。随后,Intel也推出了兼容x64架构的处理器。目前,绝大多数......
  • Python | 文件处理
    文件的读写文件对象在python中用open()可以创建一个文件对象。open()使用方法:open(file,mode='r',buffering=-1,encoding=None,errors=None,newline=None,closefd=True,opener=None)参数说明:file:必需,文件路径(相对或者绝对路径)。mode:可选,文件打开模式(常用)buf......
  • 7. 统一结果封装
    1.表现层与前端数据传输协议定义SSM整合以及功能模块开发完成后,接下来,我们在上述案例的基础上分析下有哪些问题需要我们去解决下。首先第一个问题是:在Controller层增删改返回给前端的是boolean类型数据​​在Controller层查询单个返回给前端的是对象​​在C......
  • 传奇开服技术基础十条处理办法110.42.2
    1:怎么添加GM,游戏管理员!答:在游戏版本路径中的-MirServerMir200EnvirAdminList.txt文本内,从这里增添需要重新启动,为了方便都在M2server中进行增添M2-查询-文件列表信息-管理员列表中进行增添,增添好了即时生效.人物在游戏里需要小退开始生效!2:假如增添删除商铺物品!答:3K引擎......
  • C# 学习笔记 - 异常
    异常概述C#的异常捕获系统允许开发者将正常代码与异常处理逻辑进行分离。异常可以表示在软件执行期间出现的各种异常情况,包括内部的和外部的。外部条件导致的异常:网络故障、权限不足、内存不足、Web服务引发的异常,这些异常通常由操作系统、.NET运行时或外部应用程序引发;内......
  • XP中怎样让批处理文件运行后,不关闭dos窗口
    BAT文件最后加一行:pause因为双击运用结束后就关闭界面了======在BAT文件后面加上CMD就行了你看看BAT最后面几行有没有类似EXIT的命令,如果有,删除掉把CMD加上,或在EXIT之前加上,谢谢!!......
  • 11.9 自定义异常
    demo在项目开发中,会大量接触自定义异常本节案例,综合本章节很多案例。classBombExceptionextendsException{//自定义强制处理异常 publicBombException(Stringmsg){ super(msg);//调用父类构造 }}classFood{ publicstaticvoideat(intnum)throwsBombE......
  • 11.7 异常处理模型
    demo1这种模型,开发中经常用classMyMath{ publicstaticintdiv(intx,inty)throwsException{ //异常抛出 inttemp=0; System.out.println("***【START】除法计算开始***"); //开始提示信息 try{ temp=x/y; }catch(Exceptione)......
  • 11.3 处理多个异常
    demopublicclassJavaDemo{ publicstaticvoidmain(Stringargs[]){ System.out.println("【1】******程序开始执行******"); try{ intx=Integer.parseInt(args[0]); //初始化参数转为数字 inty=Integer.parseInt(args[1]); //初始化参数转为......
  • 11.2 异常处理
    demo1publicclassJavaDemo{ publicstaticvoidmain(Stringargs[]){ System.out.println("【1】******程序开始执行******"); try{ System.out.println("【2】******数学计算:"+(10/0)); //执行除法计算 }catch(ArithmeticExceptione){ /......