这里我就列出一些我认为应当遵守的,并且添加一些我自己在工作中的一些感受,如果照着文档抄,那完全变成练习打字了,浪费读者时间,如果你也认同我的看法,或者和我有类似感受,可以点个关注,共同进步,如果有不同意见,欢迎指出。
一、错误码
这个我就不列举阿里的规范要求了,因为大家都有自己的一套规则,这是阿里的定义方式,我觉得没有必要完全模仿。
首先错误码的作用就是,让我们能够快速定位到错误所在位置,并且在定义异常时能够有一个统一的标准。
错误码在设计指定规则的时候应当考虑支持不断追加的方式,以适应项目不断迭代带来的问题。
基于目前基本都是前后端分离的模式,基于 RESTful 风格,后端都是统一返回一个JSON格式的响应,而这个返回对象,通常包含,code,data, message 这些字段。
如果一切正常,也要返回一个错误码,阿里规定 00000,这个可以按照自己随便定义,我们规定的是20000,对应 Http 响应码 200
先介绍一下阿里的错误码制定规则:
- 使用字符串类型,共 5 位。错误来源 + 四位数字编码
- 错误来源分 用户/当前系统/第三方服务,分别对应 A/B/C
- 数字编码从 0001 到 9999,大类之间步长预留 100
- 错误码又分为三级,
- 每个来源的第一号,被定义为一级宏观错误码,比如 A0001/B0001/C0001
- 每个系统或者说模块中的开始被定义为二级宏观错误码,比如 A0100/A0200/B0300/C0400 (由步长决定)
- 具体在范围间的被定义为三级错误码,比如:A0101/B0102/C0103 这些。
- 00000 表示一切正常
- 错误码由统一平台管理,申请,审批生效。
- 避免随意定义新的错误码,尽可能在已经定义的错误码中找到语义相近的使用即可
我们可以参考阿里这套规则制定我们自己的一套规则,我之前会有疑惑步长100会不会不够用,后来想想,连阿里都够用了,我们这小公司还能超了不成,纯属多虑了。
二、异常处理
- 不要去捕获一下可以通过预检规避的 RuntimeException,比如一些查询结果可能为 null 的情况。
- 异常本来设计的初衷是解决一些意外情况,不要把他当作一种判断手段,我之前就碰到过这么写代码的同事,大概逻辑是,正常情况执行A流程,如果有异常则走B流程,这样做违背了异常设计的初衷,而且效率也比条件判断低很多。
- 捕获异常必要要处理它,不要捕获了什么都不做,这个我在 CodeReview 的时候就发现过,有的为了程序不抛出异常,手动 try 捕获,但是不做任何处理,这样有严重的安全隐患,隐藏了代码执行真实情况,如果不想处理可以抛出给上一层调用者。
- 在事务场景中,如果手动 catch 要注意可能会导致事务无法回滚。
- finally 块对资源对象或者流对象关闭的时候,有异常也要 try-catch
- finally 中不要写 return,否则会导致 try 块中的 return 失效。
- 捕获异常与抛出的异常要相匹配,或者捕获的是抛出异常的父类
- 一些注意NPE的场景
- 返回类型是基本类型,但是 return 的时候给的是包装类型,自动拆箱的时候可能产生 NPE
- 数据库查询结果可能是 null
- 集合里的元素即使是 isNotEmpty,取出的数据也可能是 null
- 远程调用返回对象时,一路要进行非空判断
- 对于 Sesson 中获取的数据,建议进行 NPE 判断
- 级联调用的时候,obj.getA().getB().getC(),易产生NPE
- 对于抛出去的异常使用具有业务含义的自定义异常
三、日志规约
- 使用 SLF4J,有利于维护和各个类的日志处理方式统一
- 日志文件至少保存 15 天,当天日志以 “应用名.log” 来保存,保存在/{统一目录}/{应用名}/logs/目录下,历史日志格式:{logname}.log.{保存日期},日期格式 yyy-MM-dd
- 根据国家法律,网络运行状态、网络安全事件、个人敏感信息操作等相关记录,留存的日志不少于六个月,并且进行网络多机备份。
- 应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:appName_logType_logName.log。logType:日志类型,如stats/monitor/access等;logName:日志描述。这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。
- 在日志输出时,字符串变量之间的拼接使用占位符的方式。
说明:因为String字符串的拼接会使用StringBuilder的append()方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。正例:logger.debug(“Processing trade with id:{} and symbol:{}”,id,symbol);
- 对于trace/debug/info级别的日志输出,必须进行日志级别的开关判断:
- 避免重复打印日志,浪费磁盘空间,务必在日志配置文件中设置 additivity=false
- 日志打印时禁止直接用JSON工具将对象转换成String,使用对象的 toString 方法