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入门到精通教程
查看: 361|回复: 0

SpringBoot系列: Spring项目异常处理最佳实践

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-5-12 08:07:27 | 显示全部楼层 |阅读模式

    ===================================
    自定义异常类
    ===================================
    稍具规模的项目, 一般都要自定义一组异常类, 这样做的好处是:
    1. 可以充分利用异常的中断特性, 简化代码的逻辑控制.
    2. 在自定义的异常类, 可以设置 BusinessErrorCode 和 error message, 有了统一的 BusinessErrorCode, 排查和联调沟通就更容易了.

    Java 异常的 Root 是 Throwable, 其下有 Error 和 Exception. Error 是 JVM 级的致命错误, 应用系统内部一般不用关心这类错误. Exception 是异常的父类, 其下分为两类, 一类是 Runtime Exception, 一类是 Checked Exception. Checked Exception 是那些编译器能检查到的异常, 如果一个函数中抛出了这类异常, 我们要么 catch 它, 要么在函数签名上继续抛出去, 否则程序将不能编译通过.

    Runtime Exception 有, 包括 RuntimeException 和它的子类. 比如 ArrayIndexOutOfBoundsException/ClassCastException/被 0 除等.
    Checked Exception 有: Exception 类和所有非 RuntimeException 类的异常都属于 checked exception, 比如 IoException 等.

    自定义异常类的基类如何选择?
    自定义异常类应该继承自 RuntimeException 类, 原因有:
    1. Spring 事务控制只支持 RuntimeException 类的异常.
    2. 如果我们想要在函数加 throw Exception 签名, Java 语言已经提供了非常丰富的选择, 总能找到一个很合适的类, 而不需要再自定义一个.


    ===================================
    自定义类的最佳实践:
    ===================================
    1. 先定义一个基类 BusinessException, 继承自 RuntimeException.
    2. 定义一套 BusinessErrorCode 枚举类型, 包含 BusinessErrorCode/HttpStatus/BusinessErrorMessage, 这里的 BusinessErrorCode 不同于 HttpStatus, 它是业务上的错误代码 (int 型).
    2. 在 BusinessException 基类上, 加上绑定 BusinessErrorCode 枚举类型的机制.
    3. 基于 BusinessException 定义一组子类, 比如 UserNotLoginException/PermissionForbiddenException/DataNotFoundException 等等, 并将这些子类加入到一个 BusinessExceptionEnum 枚举中, 方便使用.

     

    ===================================
    Spring 项目数据验证最佳实践
    ===================================
    1. 针对 UI 输入检查, 如果 js 前端检查有困难, 可以在 Controller 层使用 Pojo validation 手段做检查, 然后前端使用 ajax 拿到校验结果. 检查过程没有触发 UI 完整渲染, 用户体验会很好.
    2. Controller 层使用 validation 进行检查, 可以在视图函数的形参上检查, 或者在视图函数内部检查. 
    3. Service 层, 使用 org.springframework.util.Assert 进行数据验证, 比如 Assert.notNull(user, "user is not null.");
    4. DAO 层, 不做任何数据验证, 因为所有数据问题应该在Service层或Controller层做个验证.


    ===================================
    Spring 各层封装的手法
    ===================================
    1. DAO 层, 函数的形参最好以 DO 类做参数, 而不是传入很多个字段参数. 这样的好处是, 避免Table增删字段, DAO层函数定义也要跟着修改, 上层的调用代码也要修改.
    2. DAO 层, insert 和 update 要独立为两个函数. 到底是新增还是更新, 应由 Service 层进行逻辑控制.
    3. Service 层函数的形参, 到底是使用 DO 类, 还是简单的属性清单, 看具体情况吧.


    ===================================
    Spring 日志和异常处理的最佳实践
    ===================================
    异常处理的基本思路是: 早抛出, 晚捕获. 日志输出的基本思路是, 详尽但不冗余.
    落实到具体的项目中, 在不同分层中, 应采用不同的规则, 一般的分层有: DAO -> Service -> Controller -> 统一异常 Controller 层.

    1. DAO 层:
       (1) 尽量不 catch 任何异常, 该向上抛就抛.
       (2) 不用记录 log 日志, 或者仅使用 logger.debug() 记录

    2. Service 层的做法:
        (1) @Transactional 注解应该加在 Service 层上.
        (2) 对于一些关键问题, 比如 Checked Exception 或者数据的问题, 应该及时 throw new BusinessException 异常, 以确保事务完整.
        (3) throw new BusinessException 时的日志, 为了避免日志重复, 不需要 log 日志输出.
        (4) Service 层一般的日志级别, 应该用 logger.debug() 记日志.
        (5) Service 层函数的返回值应该是 Optional 类型, 方便 Controller 做 null 判断.


    3. Controller 层:
       (1) Controller 层负责组装 Service, 在关键步骤上应该加日志输出 (info 级别)
       (2) Controller 层不应再主动 throw 异常.
    4. 统一异常 Controller 层:
       (1) 通过 json 或 UI 返回详细的报错信息, 包括 HttpStatus 和详尽的 BusinessErrorCode/BusinessErrorMessage 以及 DetailErrorMessage(来源于 exception.getMessage()), 甚至包括 exception 的 stack trace.
       (2) 对于 Exception 类的异常, 说明这是我们预料之外的报错, 应该使用 logger.error() 级别记录;
       (3) 对于 BusinessException 和子类的异常, 则说明我们的程序已经预料到了, 事物该回滚也已经回滚了, 所以应该以 logger.warn() 或 logger.info() 记录日志.
       (4) 对于 Spring Boot 缺省的 /error 进行定制, 增加一些"系统主页"和"返回"的链接, 改善用户体验.


    ===================================
    参考:
    ===================================
    https://blog.csdn.net/aiyaya_/article/details/78725755
    https://blog.csdn.net/aiyaya_/article/details/78989226
    https://blog.qinchuan.io/experience/2018/10/11/spring-boot-restful-api-error-handling-in-practice.html
    https://zhuanlan.zhihu.com/p/38114882
    http://tengj.top/2018/05/16/springboot13/

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

    使用道具 举报

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

    本版积分规则

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

    GMT+8, 2024-5-20 07:39 , Processed in 0.065126 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

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