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 ...
 
随机推荐
- 网络编程_tcp与dup协议简单应用
			
老师的博客:http://www.cnblogs.com/Eva-J/articles/8066842.html 计算机网络基础 :http://www.cnblogs.com/Eva-J/artic ...
 - 【Linux基础】查看某一端口是否开放(1025为例)
			
1.使用lsof 命令来查看端口是否开放 lsof -i:1025 //如果有显示说明已经开放了,如果没有显示说明没有开放 lsof(list open files)是一个列出当前系统打开文件的工具. ...
 - Python开发【字符串格式化篇】
			
1.百分号 __author__ = "Tang" # + 号 拼接 msg = "i am " + " tang" print(msg) ...
 - 文本分类实战(八)—— Transformer模型
			
1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...
 - CentOS 6.5 minimal 安装配置VMware tools
			
1.登录到系统,切换到root账户 2.配置网络 minimal版本默认不启动网络,所以要自己配置. 配置过程很简单,编辑配置文件 vi /etc/sysconfig/network-script/i ...
 - pyspider常见错误
			
安装完爬虫框架pyspider之后,使用pyspider all 命令,可能会出现以下错误: - Deprecated option 'domaincontroller': use 'http_aut ...
 - 使用offsetof对结构体指针偏移操作
			
题目来自于COMP20003 Tutorial 2: Program m ing Challenge 2.2 The technology stack at Hidebound Inc. uses a ...
 - 使用.net core efcore根据数据库结构自动生成实体类
			
源码 github,已更新最新代码 https://github.com/leoparddne/GenEntities/ 使用的DB是mysql,所有先nuget一下mysql.data 创建t4模板 ...
 - Unit 3.标签的分类和嵌套规则
			
一.标签分类 HTML中标签元素三种不同类型:块状元素,行内元素,行内块状元素. 常用的块状元素: <div> <p> <h1>~<h6> <ol ...
 - zookeeper报错java.net.ConnectException: Connection refused: no further information
			
zookeeper报错java.net.ConnectException: Connection refused: no further information 这是在linux 启动 https:/ ...