===================================
自定义异常类
===================================
稍具规模的项目, 一般都要自定义一组异常类, 这样做的好处是:
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项目异常处理最佳实践的更多相关文章

  1. Spring Boot Admin最佳实践

    本文不进行Spring Boot Admin入门知识点说明 在Spring Boot Actuator中提供很多像health.metrics等实时监控接口,可以方便我们随时跟踪服务的性能指标.Spr ...

  2. 开涛spring3(7.5) - 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践

    7.5 集成Spring JDBC及最佳实践 大多数情况下Spring JDBC都是与IOC容器一起使用.通过配置方式使用Spring JDBC. 而且大部分时间都是使用JdbcTemplate类(或 ...

  3. Java异常处理最佳实践

    总结一些Java异常的处理原则 Java异常处理最佳实践 不要忘记关闭资源 在finally里关闭资源 public void readFile() { FileInputStream fileInp ...

  4. SpringBoot系列——自定义统一异常处理

    前言 springboot内置的/error错误页面并不一定适用我们的项目,这时候就需要进行自定义统一异常处理,本文记录springboot进行自定义统一异常处理. 1.使用@ControllerAd ...

  5. Java异常处理最佳实践及陷阱防范

    前言 不管在我们的工作还是生活中,总会出现各种“错误”,各种突发的“异常”.无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们 ...

  6. OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(上)

    这篇博文难产了很久,原来是打算一周更新一篇的,上周原计划写MVC,但是写了一半,发现带入了太多的细节,不太符合这个入门系列的主题. 当我们学习一个新的技能的时候,如果一开始就面对大量的细节,很容易陷入 ...

  7. OPEN(SAP) UI5 学习入门系列之二: 最佳实践练习(下)

    上期我们完成了一个简单的主从页面,但是页面是静态的,不能交互,功能也很简单,只有一个销售订单的列表. 我们今天就一鼓作气把代码全都写完,由于本次的代码量较大,所以只对重点代码部分进行讲解. 具体每个文 ...

  8. go项目dockerfile最佳实践

    1. 前言 2. 不需要cgo情况下的最佳实践 3. 依赖cgo情况下的最佳实践 1. 前言 这几天在构建golang编写的web项目中,关于dockerfile编写的一些总结 可能是单纯我比较菜(大 ...

  9. 【SpringMVC】SpringMVC系列15之SpringMVC最佳实践

    15.SpringMVC最佳实践 15.1.遵循Restful API最佳实践 参考:http://segmentfault.com/a/1190000002949234 15.2.统一返回字段 15 ...

随机推荐

  1. CentOS7 Docker私有仓库搭建及删除镜像 【转】

    文章来源:centos7 Docker私有仓库搭建及删除镜像 如果不想用私有镜像库,你可以用docker的库 https://hub.docker.com 环境准备 环境:两个装有Docker 17. ...

  2. Elixir 分布式平台

    概述 分布式平台的核心在于并发,容错. 而 Elixir 的优势正是在于对于并发和容错的处理. 分布式模型 CSP(Communicating Sequential Process) 模型 :: 多个 ...

  3. mysql导出表的字段及相关属性

    需要导出数据库中表的字段及属性,制成表格保存到word中 首先找到要导的库, 在查询页面输入sql SELECT COLUMN_NAME 列名, COLUMN_TYPE 数据类型, DATA_TYPE ...

  4. bsp makefile2

    1. grep "bsp_dir" -r ./  -s  --exclude-dir "*.git" 用这个加快目录定位-- 2.编译所有子目录 for dir ...

  5. zabbix,php,nginx,mysql源码安装 神仙操作

    →软件包 mkdir /soft/ cd /soft ♦下载以下软件包 nginx-1.14.2.tar.gz wget http://nginx.org/download/nginx-1.14.2. ...

  6. SpringBoot开发案例之整合Activiti工作流引擎

    前言 JBPM是目前市场上主流开源工作引擎之一,在创建者Tom Baeyens离开JBoss后,JBPM的下一个版本jBPM5完全放弃了jBPM4的基础代码,基于Drools Flow重头来过,目前官 ...

  7. Array.prototype.reduce()

    reduce() 方法接收一个函数作为累加器(accumulator),数组中的每个值(从左到右)开始缩减,最终为一个值. arr.reduce([callback, initialValue]) c ...

  8. nginx代理部署Vue与React项目

    nginx代理部署Vue与React项目 一,介绍与需求 1.1,介绍 Nginx (engine x) 是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务.Nginx是由 ...

  9. Unit 6.标准文档流,浮动,清除浮动以及margin塌陷问题

    一. 什么是标准文档流 文本流其实就是文档的读取和输出顺序,也就是我们通常看到的由左到右.由上而下的读取和输出形式,在网页中每个元素都是按照这个顺序进行排序和显示的,而float和position两个 ...

  10. vue.js实战——购物车练习(包含全选功能)

    vue.js实战第5章 54页的练习1 直接放代码好了,全选的部分搞了好久,代码好像有点啰嗦,好在实现功能了(*^▽^*) HTML: <!DOCTYPE html> <html l ...