SpringBoot系列: Spring项目异常处理最佳实践
===================================
自定义异常类
===================================
稍具规模的项目, 一般都要自定义一组异常类, 这样做的好处是:
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/
SpringBoot系列: Spring项目异常处理最佳实践的更多相关文章
- Spring Boot Admin最佳实践
本文不进行Spring Boot Admin入门知识点说明 在Spring Boot Actuator中提供很多像health.metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标.Spr ...
- 开涛spring3(7.5) - 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践
7.5 集成Spring JDBC及最佳实践 大多数情况下Spring JDBC都是与IOC容器一起使用.通过配置方式使用Spring JDBC. 而且大部分时间都是使用JdbcTemplate类(或 ...
- Java异常处理最佳实践
总结一些Java异常的处理原则 Java异常处理最佳实践 不要忘记关闭资源 在finally里关闭资源 public void readFile() { FileInputStream fileInp ...
- SpringBoot系列——自定义统一异常处理
前言 springboot内置的/error错误页面并不一定适用我们的项目,这时候就需要进行自定义统一异常处理,本文记录springboot进行自定义统一异常处理. 1.使用@ControllerAd ...
- Java异常处理最佳实践及陷阱防范
前言 不管在我们的工作还是生活中,总会出现各种“错误”,各种突发的“异常”.无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们 ...
- OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(上)
这篇博文难产了很久,原来是打算一周更新一篇的,上周原计划写MVC,但是写了一半,发现带入了太多的细节,不太符合这个入门系列的主题. 当我们学习一个新的技能的时候,如果一开始就面对大量的细节,很容易陷入 ...
- OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(下)
上期我们完成了一个简单的主从页面,但是页面是静态的,不能交互,功能也很简单,只有一个销售订单的列表. 我们今天就一鼓作气把代码全都写完,由于本次的代码量较大,所以只对重点代码部分进行讲解. 具体每个文 ...
- go项目dockerfile最佳实践
1. 前言 2. 不需要cgo情况下的最佳实践 3. 依赖cgo情况下的最佳实践 1. 前言 这几天在构建golang编写的web项目中,关于dockerfile编写的一些总结 可能是单纯我比较菜(大 ...
- 【SpringMVC】SpringMVC系列15之SpringMVC最佳实践
15.SpringMVC最佳实践 15.1.遵循Restful API最佳实践 参考:http://segmentfault.com/a/1190000002949234 15.2.统一返回字段 15 ...
随机推荐
- 虚拟机配置Linux上网环境
概要:在虚拟机安装CentOS6.5的环境后,配置NAT模式,修改系统文件支持上网. (1)ip地址的配置,IP地址的子网掩码为255.255.255.0. (2)网关的指定,也就是默认路由,当我们需 ...
- html5 contenteditable 实现table可编辑(网页版EXCEL)
一直想找一个免费的网页版的EXCEL插件,以便于多人共同在线编辑,始终没发现合适的. 其实自己实现类似功能也不难.参考:https://blog.csdn.net/chadcao/article/de ...
- Django-CRM项目学习(一)-admin组件
开始今日份整理 1.admin组件使用 1.1 创建django项目以及开启APP01 略 1.2 创建类 使用django自带的sqlite3的小型文件型的数据库 注:使用sqlite3类型的数据库 ...
- linux下 启动node 和关闭node
1.用forever 进行管理 npm install -g forever forever start app.js //启动 forever stop app.js //关闭 2.用自带的服务n ...
- 好程序员web前端分享18个用CSS制作出来的东西
好程序员web前端分享18个用CSS制作出来的东西,与流行的看法相反,CSS不仅仅是用来提供一个WEB页面的基本风格,以使它看起来更有吸引力.还有很多其他的事情,CSS也可以做的很好.由于它创建动画和 ...
- 使用serialize时多数据传递
class CartList(APIView): #定义编辑方法 def post(self,request): username = request.POST.get('username') # p ...
- OOM分析工具
OOM (OutOfMemoryError) 1.MAT工具 在eclipse中安装.Help>Eclipse Marketplace 搜索MAT 接下来运行程序,run configratio ...
- 基于vue现有项目的服务器端渲染SSR改造
前面的话 不论是官网教程,还是官方DEMO,都是从0开始的服务端渲染配置.对于现有项目的服务器端渲染SSR改造,特别是基于vue cli生成的项目,没有特别提及.本文就小火柴的前端小站这个前台项目进行 ...
- [模板] 匈牙利算法&&二分图最小字典序匹配
匈牙利算法 简介 匈牙利算法是一种求二分图最大匹配的算法. 时间复杂度: 邻接表/前向星: \(O(n * m)\), 邻接矩阵: \(O(n^3)\). 空间复杂度: 邻接表/前向星: \(O(n ...
- Civil 3D .NET二次开发第11章代码升级至2018版注意事项
原来涉及2017的,均需要改为2018 原来的21改为22 代码中AeccXUiLand.AeccApplication.11.0"改为AeccXUiLand.AeccApplication ...