T31实战-Day5:异常处理机制

前言
Java有很多优秀的语言特性和处理机制,但很少有人会提到异常处理机制,但是异常处理机制是Java中非常重要的组成部分,它对于保证我们系统的健壮性,可维护性等方面是非常重要的。 
本文是对孤尽T31项目中学习Java异常处理机制的笔记总结。
一、Java异常机制
1. 使用异常,日志为系统保驾护航
道路千万条,安全第一条,日志不规范,排查两行泪

异常应当描述导致当前异常发生的原因
根据异常栈应当能够快速定位到异常发生的位置
结合异常描述和异常栈应当能解决异常
2. C语言的“异常”烦恼

以上c语言的异常处理有什么问题呢?

代码可读性差,去掉注释的话,可读性就更差了
当返回值与异常值相近时,容易混淆;例如定义了异常码1,2,3等,但刚好正确返回值也有这些数据,就需要额外了解所有返回值,保证异常码跟返回值不重复
需要调用方来分析异常,增加多余的工作量;调用方同样也需要了解所有返回值代表的意思
3. Java异常处理流程


finally里发生的异常如何正确捕获和处理?
finally里发生了异常,没有捕获处理,也没有处理原异常,jvm会怎么处理2个异常呢?
4. Java异常处理机制

try代码块中抛出多种异常该怎么处理?

加多个catch捕获所有可能的异常进行处理,每种异常处理流程不一致时采用这种
分析多个异常是不是某一类子异常,并且后续处理办法是一致的,可以只抛出父类异常
5. Java异常体系

Error
一般跟java虚拟机有关,比较致命的错误,如系统崩溃,虚拟机出错误等,通常应用程序无法处理的,Error也是不可检查的
运行时异常
是RuntimeException类及其子类异常,例如空指针异常,数组越界异常,除0异常等等,这类异常是不可检查的(编译器无法预计你写的程序逻辑有没有错误),程序可选择捕获或不捕获处理,一般都是由于程序逻辑错误引起的,用户也可以自定义可预测的业务运行时异常
非运行时异常
是RuntimeException以外的异常,如IOException、SQLException以及用户自定义异常等,这类异常是可检查的(可预计会发生的异常,而一旦发生就必须捕获处理的),编译器会检查它,要求必须catch,否则不可编译通过
异常处理有一条强制规约:在调用rpc或者二方包,或者动态生成类相关方法时,捕获异常必须使用Throwable,因为有可能抛出Error或者Exception

二、异常处理设计与实践
1. 异常抛出与捕获的原则
非必要不使用异常
异常捕获主要是针对非稳定性代码,生成异常对象和处理流程也是耗性能的,在稳定性代码中不必要try-catch,也不能大而全的try-catch,而只在有必要的语句中try-catch,主要对检查异常或自定义业务异常进行捕获处理
使用描述性信息抛出异常
例如上下文信息和参数,异常产生的原因,运行时环境等等
力所能及的异常一定要处理,不要都往上抛出
异常忽略要有理有据
2. try-catch-finally流程分析


强制规约:不要在finally里写return语句
try里的return不是立即返回结果,而是先放函数栈中,待finally语句块执行完后才返回,如果finally里有赋值给返回值变量,并且有return,则会忽略try里的返回值,如果没有return,则即使有赋值语句,也不会忽略try返回值
3. try with resource关闭资源的执行流程


上面代码有个问题,在关闭out的过程中发生异常的话,就不会再关闭FileOutputStream这个资源了,因为try with resource的机制只会关闭声明的2个资源,如下图来解决多资源的关闭

try()里的资源必须是实现了Closeable的
资源是越晚使用到的,越早被关闭

上图中,a,b资源都有test方法并且里面抛出异常,也都有关闭资源的方法,当发生异常时,需要额外处理(上图选中的代码部分)才能看到a,b关闭方法里抛出的异常信息
无额外处理代码时打印的日志,只打印出a的test()的异常信息,a,b的close里的异常信息都没有打印出来
有额外处理代码的打印出来的日志
4. 特殊NPE场景及其处理对策
级联调用时易产生NPE


foreach遍历集合的异常
不要在foreach循环里进行元素的remove和add操作
foreach循环会自动跳过遍历空集合,如果有null值的集合,碰到null时注意NPE
三、日志设计
1. 日志的功能


2. 日志时效性规约


记录用户敏感信息或操作日志为何要至少存储6个月,并多机备份?用什么级别日志输出?
相关法律规定的,有一些金融方面要求保存可以更长,这是监管检查的原因;还有当发生一些纠纷的时候,可以拿出比较长时间的有效数据进行检查,作为证据,打官司等等处理;联机存储是为了防止丢失,人为损坏数据等原因;最好使用warn级别
3. 日志记录规约
系统应依赖使用日志框架(SLF4J,JCL)的API而不是具体日志库的
日志框架大神Ceki Gulcu,主要有 Log4j、Logback 和 slf4j
最早96年:log4j
2002年:sun推出JUL,apache推出JCL


Slf4j绑定到各种日志库的流程
在日志输出时,字符串变量之间的拼接方式使用占位符方式
日志打印时禁止直接使用JSON工具将对象转换成String
尽量用英文来描述日志错误信息 - 推荐
4. logback框架使用
核心配置对象及其属性分类

关键类图分析

线程模型分析

5. 日志通知

6. 日志输出规约
日志级别开关判断:对于trace/debug/info级别的日志输出,必须进行日志级别的开关判断
异常日志信息要完整:包括2类信息,案发场景信息;异常堆栈信息
避免重复打印日志:浪费磁盘空间,在日志配置文件中设置additivity=false
扩展日志单独存储:如打点,临时监控,访问日志等
错误日志单独存储:业务日志与错误日志分开存储
四、错误码设计
1. 错误码规约
定义时要有字母也要有数字
要分级分类管理

在完善的系统中甚至会开发错误码配置模块来管理
不能直接输出给用户作为提示信息使用
不要与业务架构或组织架构挂钩
使用者避免随意定义新的错误码
便于不同语言的开发者之间协作
五、异常处理和日志综合实践
1. 在Controller层统一捕获异常


不是每一层都处理,都是往上层抛出,一般需要对异常信息,日志进行包装处理在抛出去
在分布式部署多台机器时,异常日志则需要每层单独记录的,便于追溯来源和链路跟踪
2. 全局异常处理组件


3. API层异常设计实践


4. Service层异常设计实践


5. DAO层异常日志实践
使用继承自RuntimeException的通用DaoException封装DAO层的异常并向上抛出
在DAO框架层面有选择的记录数据操作的有效信息,比如:每次操作的原始sql语句以及其执行时间
6. 使用MDC实现轻量级调用链路跟踪


7. 使用有限的异常类处理业务中复杂多变的无限可能
定义继承RuntimeException的通用ServiceException业务异常
结合与业务关联的ErrorCode实现复杂多变的业务异常需求
8. 降低系统的维护难度,过度设计,冗余手段
合理的领域划分
通用模块单独拆分
合适的工程结构
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值