目录
零、引言
一、基础
二、集合
三、并发
四、日志
五、安全
零、引言
规范等级:
|
一、基础
序号 | 等级 | 规范 | 示例 | 说明 | |||
---|---|---|---|---|---|---|---|
1 | 强制 | 在POJO类中定义布尔类型成员变量时,禁止用is作变量名前缀。 | 反例:
| is作变量名前缀的布尔型成员变量,在一些IDE(如:IDEA)中,默认生成的getter方法与变量名相同,导致部分框架(如:Jackson、Fastjson)在反向解析时会引发“找不到指定成员变量名”的错误。 | |||
2 | 强制 | 在对象之间做相等比较时,应当使用Objects工具类(java.util.Objects)的equals方法。 | 正例:
反例:
| 当对象为null时,直接调用equals会出现空指针异常。 注意:Objects的equals方法内部会利用参数对象的equals方法进行比较,对数组、集合之类的对象并不会做内部元素的一一比较。 | |||
3 | 强制 | 在BigDecimal之间做等值比较时,禁止使用equals方法。 | 正例:
反例:
| BigDecimal的equals方法会比较精度,如1.0与1.00比较的结果为false,推荐使用其 compareTo方法做比较。 | |||
4 | 强制 | 在浮点数之间做等值比较时,基本类型禁止使用==,包装类型禁止使用equals。 | 正例:
反例:
反例:
| 浮点数先转成BigDecimal,再用compareTo方法做比较,以避免精度问题影响结果。 浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数。 | |||
5 | 强制 | 将浮点数转换为BigDecimal 时,禁止直接使用构造方法。 | 正例:
反例:
| BigDecimal的浮点数构造方法存在精度损失风险,在精确计算或值比较的场景中会导致业务逻辑异常。 注意:和上一条的正例不同的是,前者在经过浮点计算后已经形成了误差,在转换时用toScaledBigDecimal方法限定了精度。在此处,是为了避免因直接利用浮点数构造而导致的误差。 | |||
6 | 强制 | 在空指针异常易发的场景中,应当对对象做null判断。 | 正例:
反例:
| 在对象未判断null的情况下直接引用,容易发生空指针异常,推荐用Optional类更优雅的处理null对象。 一些空指针异常(NPE)易发的场景:
| |||
7 | 强制 | 在日期格式化时,应当使用DateUtils工具类 | 正例
反例
| DateUtils包装了常用的日期格式,避免了手动格式化时的误用风险,如:年份格式化误书写成YYYY,如本周存在跨年的情况,返回的就是下一年。 | |||
8 | 强制 | POJO类属性,禁止使用基本数据类型。 | 正例
反例:
| POJO类属性使用包装数据类型有如下优点:
|
二、集合
序号 | 等级 | 规范 | 示例 | 说明 | ||
---|---|---|---|---|---|---|
1 | 强制 | 在需要对List的subList方法返回结果进行遍历、增加、删除元素时,禁止直接变更原List中的元素。 | 反例:
| List的subList方法返回的是一个List的内部类对象,它是List的一个视图,对原List所有的操作都会反映到这个对象上。 同时,对原List进行元素的增加或删除,会被计数(modCount,结构变更次数),此数值和原subList时记录的数值(expectedModCount)不一致,会引发ConcurrentModification Exception异常。 | ||
2 | 强制 | 对集合进行for循环时,禁止在循环体内用remove/add方法。 | 正例:
反例:
| 若在for循环体内对集合元素进行remove/add操作,可能导致异常,建议使用iterator方式处理。 |
三、并发
序号 | 等级 | 规范 | 示例 | 说明 | ||
---|---|---|---|---|---|---|
1 | 强制 | 动态线程池只允许使用通义管理平台定义的(比如Poseidon),禁止自行创建。 | 正例:
反例:
| 通过Poseidon创建的线程池将能较好的进行管理和监控:
| ||
2 | 强制 | 在使用线程池时,禁止将拒绝策略设置为DiscardPolicy。 | 反例:
| 若配置了DiscardPolicy,当线程池队列排满且已达到了最大线程数后,新增任务会被直接丢弃,无任何提示,并且在结合future.get()运行时,存在阻塞的风险。 | ||
3 | 强制 | 父子任务禁止使用同一个线程池。 | 反例:
| 父子任务使用同一个线程池容易相互影响,线程数达上限时,子任务等待线程资源,而同时,父任务因子任务未完成,其资源得不到释放,最终可能导致相互等待或死锁。 | ||
4 | 强制 | 在多线程环境下,禁止直接使用HashMap。 | 正例:
反例:
| HashMap是线程不安全的,在容量不够进行resize时,可能因并发出现死链,导致CPU飙升。 |
四、日志
序号 | 等级 | 规范 | 示例 | 说明 | ||
---|---|---|---|---|---|---|
1 | 强制 | 日志级别只允许使用ERROR、WARN、INFO、DEBUG。 | 正例:
|
| ||
2 | 强制 | 业务受损或预期外的异常场景,应当打印ERROR日志。 | 正例:
反例:
| ERROR日志用于描述异常不可控的场景,当该类异常发生的时候会给业务和系统带来伤害,需要第一时间告警并介入排查修复。 | ||
3 | 强制 | 业务不受损且预期内的异常场景,应当打印WARN日志。 | 正例:
| WARN日志用于描述异常可控的场景,当该类异常发生的时候不会给业务和系统带来伤害,用于记录和观测,指导进一步处理。 | ||
4 | 推荐 | 打印日志时,建议使用占位符的方式拼装内容。 | 正例:
反例:
| ”+“ 拼接会多次调用StringBuilder的append()方式,每一次append的时候会计算字符串的长度以及重新分配一次内存,对性能有一定的损耗。 此外,“+”拼接方式无论本条日志是否打印都会计算长度和分配内存,而占位符的方式仅在打印的时候才进行内存分配。 | ||
5 | 推荐 | 打印日志时,不建议使用JSON工具将对象转换成String。 | 正例
反例:
| 如果对象里某些get方法被覆写,存在抛出异常的风险,进而影响正常业务流程。 | ||
6 | 推荐 | 异常日志内容中应当包含三要素:异常场景、异常数据、异常堆栈。 | 正例:
反例:
| 异常日志内容应记录关键的信息(异常场景、异常数据、异常堆栈),为问题排查提供有效帮助,能更高效的处理线上故障。 三要素包含: 异常场景:出现异常的业务场景说明。 异常数据:出现异常的数据(比如下单场景,需要记录商品ID、用户ID等信息)。 异常堆栈:异常堆栈信息。 |
五、安全
序号 | 等级 | 规范 | 示例 | 说明 | ||
---|---|---|---|---|---|---|
1 | 强制 | 用户敏感数据禁止直接展示、禁止用Get方式提交。 | 反例:
| 手机号、银行卡卡号、身份证、车牌、车架号等都属于用户敏感信息,不能直接展示。 脱敏方式:
禁止用Get方式提交,这种方式在URL上带有敏感数据,将会在wan/lan日志中出现这些元数据。 | ||
2 | 强制 | 用户输入的参数,禁止直接拼接到SQL访问数据库。 | 反例:
| 用户输入的参数可能带有SQL片段,存在SQL注入的风险,需要使用参数绑定的技术来防范。 | ||
3 | 强制 | 未经许可,禁止外发公司任何程序代码。 | 反例:
| 程序代码属于公司资产,在未经许可的情况下不得以任何方式(邮件、IM软件、纸质打印等)向外传输或公开,包括但不仅限于:
|