Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 276|回复: 0

springboot 全局异常捕获,异常流处理业务逻辑

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-9 11:52:00 | 显示全部楼层 |阅读模式

    前言

    上一篇文章说到,参数校验,往往需要和全局的异常拦截器来配套使用,使得返回的数据结构永远是保持一致的。参数异常springboot默认的返回结构:

    {
     "timestamp": "2019-04-25T13:09:02.196+0000",
     "status": 400,
     "error": "Bad Request",
     "errors": [
     {
     "codes": [
     "Pattern.param.birthday",
     "Pattern.birthday",
     "Pattern.java.lang.String",
     "Pattern"
     ],
     "arguments": [
     {
     "codes": [
     "param.birthday",
     "birthday"
     ],
     "arguments": null,
     "defaultMessage": "birthday",
     "code": "birthday"
     },
     [],
     {
     "defaultMessage": "\d{4}-\d{2}-\d{2}",
     "codes": [
     "\d{4}-\d{2}-\d{2}"
     ],
     "arguments": null
     }
     ],
     "defaultMessage": "需要匹配正则表达式"\d{4}-\d{2}-\d{2}"",
     "objectName": "param",
     "field": "birthday",
     "rejectedValue": "apple",
     "bindingFailure": false,
     "code": "Pattern"
     }
     ],
     "message": "Validation failed for object='param'. Error count: 1",
     "path": "/validate/notblank"
    }

    不管是正常的情况,还是异常的情况,对于前端(或者app)来说,最好返回值的结构都是一致的,这样才方便解释。

    定义一个BaseResult类,定义返回值的数据结构

    public class BaseResult {
     private int code;
     private String message;
     private Object data;
     // 省略getter setter方法,全参构造方法
    }

    不管什么接口,都采用这样的数据结构返回给前端。比如约定code为0时是成功,其他错误定义出具体的错误码,message放错误信息,data对象放相应的数据。

    定义全局异常处理器GlobalExceptionHandlerAdvice

    @RestControllerAdvice
    public class GlobalExceptionHandlerAdvice {
    
    }

    使用RestControllerAdvice可以标识一个类为异常捕获类。

    捕获异常

    通过参数异常的测试,可以知道参数有异常时会抛出org.springframework.web.bind.MethodArgumentNotValidException。我们现在手动捕获 这个异常,并且返回一个BaseResult格式的响应。

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BaseResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
     BindingResult result = e.getBindingResult();
     FieldError fieldError = result.getFieldError();
     String defaultMessage = fieldError.getDefaultMessage();
     return new BaseResult(11000, defaultMessage, null);
    }

    使用ExceptionHandler可以捕获异常类型。这里捕获了参数错误会抛出的异常,然后返回了自定义的结果。这里错误码为随便填写,真实开发,建议定义一个错误码枚举类。

    效果如下:

    【快学springboot】5.全局异常捕获,异常流处理业务逻辑

     

    返回的结果就比较友好了,前端处理起来也方便。

    异常流处理业务逻辑

    使用异常来处理业务逻辑,会使代码写起来更加流畅。比如说,一个删除用户数据的方法,返回值为void(无返回值),但是当传入的用户id不存在的时候,就应该返回一个用户不存在的结果,这对于void返回值的方法来说,显得无能为力。但是,使用异常流来处理该业务逻辑,会变得非常简单。我们直接抛出一个自定义异常,然后在异常捕获器上捕获该异常,再把结果返回给前端即可。

    定义一个WebException

    public class WebException extends RuntimeException {
     private int code;
     private String errorMsg;
     public WebException(int code, String errorMsg) {
     super(errorMsg);
     this.code = code;
     this.errorMsg = errorMsg;
     }
     @Override
     public synchronized Throwable fillInStackTrace() {
     return this;
     }
     // 省略getter setter方法
    }

    这里定义了一个运行时异常,重写了fillInStackTrace方法,重写该方法是不保留异常信息栈。因为我们使用该异常来处理业务逻辑,都是我们手动抛出的,所以也不需要保存异常信息栈了,这会提升性能。

    在异常捕获器添加WebException异常捕获

    @ExceptionHandler(WebException.class)
    public BaseResult handleWebException(WebException e) {
     return new BaseResult(e.getCode(), e.getErrorMsg(), null);
    }

    模拟一段业务逻辑,抛出WebException

    在之前的UserController类,修改之前写的deleteUser方法,如下:

    @DeleteMapping(value = "/{userId}")
    public Object deleteUser(@PathVariable(value = "userId") Integer userId) {
     if (userId == 0) {
     throw new WebException(-1, "用户不存在");
     }
     return new BaseResult(1, "成功", null);
    }

    这里定义一个delete请求的接口,接收一个userId参数,如果userId等于0,则返回该用户不存在。测试结果如下:

    当userId为0时,提示用户不存在

    【快学springboot】5.全局异常捕获,异常流处理业务逻辑

     

    当userId为1时,提示成功.

    【快学springboot】5.全局异常捕获,异常流处理业务逻辑

     

    总结

    这里实现了全局异常捕获,并且介绍了异常流处理业务逻辑。这里只是一个小demo,还有很多待改进的地方。比如说,我没有定义一个错误码枚举类。在定义了错误码枚举类的前提下,修改构造BaseResult的模式,可以采用静态工厂模式来构造等。这里就不展开讨论了

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-2 10:45 , Processed in 0.060212 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表