为什么要做代码Review?

  • 进步代码质量,进步自身水平
  • 及早发现潜在缺点与Bug,,降低事端本钱
  • 促进团队内部常识同享,进步团队全体水平
  • 保证项目组人员的杰出交流
  • 防止开发人员犯一些很常见,很遍及的过错

不要嫌麻烦和浪费时间,其实只需坚持下来,团队收益会十分大。

1. 增加必要的注释

其实,写代码的时分,没有必要写太多的注释,因为好的办法名、变量名,便是最好的注释。以下便是总结的一些注释标准:

  • 一切的类都必须增加创建者和创建日期,以及简略的注释描绘
  • 办法内部的杂乱业务逻辑或许算法,需求增加清楚的注释
  • 一般状况下,注释描绘类、办法、变量的作用
  • 任何需求提示的警告或TODO,也要注释清楚
  • 假如是注释一行代码的,就用//;假如注释代码块或许接口办法的,有多行/* **/
  • 一块代码逻辑假如你站在一个陌生人的角度去看,第一遍看不懂的话,就需求增加注释了
/**
*@authordidi
*@date2023/04/225:20PM
*@desc完成类,用于demo展示
*/
publicclassDidiClass{

/**
*这它将两个价格整数相加并回来结果。
*
*@paramx第一个整数
*@paramy第二个整数
*@return两个整数的和
*/
publicintsellTianLuo(intx,inty){
returnx+y;
}
}

2.日志打印标准

日志是快速定位问题的好帮手,是撕逼和甩锅的利器!打印好日志十分重要。假如代码评定的时分,这些日志标准没恪守,就需求修正:

  • 日志等级选择不对。常见的日志等级有error、warn、info、debug四种,不要反手便是info哈
  • 日志没打印出调用办法的入参和呼应结果,尤其是跨体系调用的时分。
  • 业务日志没包含要害参数,如userId,bizSeq等等,不方便问题排查
  • 假如日志包含要害信息,比方手机号、身份证等,需求脱敏处理
  • 一些不契合预期的状况,如一些不知道反常(数据库的数据反常等),又或许不契合业务预期的特别场

3. 命名标准

Java代码的命名应该明晰、简洁和易于了解。咱们代码评定的时分,要留意是否有命名不标准,不明晰的代码。下面是一些命名标准的主张:

  • 类和接口应该运用首字母大写的驼峰命名法
  • 办法和变量应该运用小写的驼峰命名法
  • 常量应该运用全大写字母和下划线
  • 开发者是不是选择易于了解的称号给变量、类和办法进行命名

4.参数校验

咱们代码评定的时分,要留意参数是否都做了校验,如userId非空查看、金额规模查看、userName长度校验等等。一般咱们在处理业务逻辑的时分,要遵循先查看、后处理的准则。

假如你的数据库字段userName设置为varchar(16),对方传了一个32位的字符串过来,你不校验参数,刺进数据库直接反常了。

许多bug都是因为没做参数校验形成的,这是代码评定要点重视的

5. 判空处理

获取目标的属性时,都要判空处理。要不然许多时分会呈现空指针反常。

if(object!=null){
Stringname=object.getName();
}

假如你要遍历列表,也需求判空

if(CollectionUtils.isNotEmpty(tianLuolist)){
for(TianLuotemp:tianLuolist){
//dosomething
}
}

6. 反常处理标准

杰出的反常处理可以保证代码的可靠性和可维护性。因而,反常处理也是代码评定的一项重要标准。以下是一些反常处理的主张:

  • 不要捕获通用的Exception反常,而应该尽或许捕获特定的反常
  • 在捕获反常时,应该记录反常信息以便于调试
  • 内部反常要承认终究的处理方式,防止不知道反常当作失利处理。
  • 在finally块中释放资源,或许运用try-with-resource
  • 不要运用e.printStackTrace(),而是运用log打印。
  • catch了反常,要打印出具体的exception,不然无法更好定位问题
  • 捕获反常与抛出反常必须是彻底匹配,或许捕获反常是抛反常的父类
  • 捕获到的反常,不能忽略它,要打印相对应的日志
  • 留意反常对你的代码层次结构的侵染(早发现早处理)
  • 自定义封装反常,不要丢掉原始反常的信息Throwable cause
  • 留意反常匹配的顺序,优先捕获具体的反常
  • 对外供给APi时,要供给对应的过错码
  • 体系内部应该抛出有业务含义的自定义反常,而不是直接抛出RuntimeException,或许直接抛出ExceptionThrowable。

7. 并发操控标准

  • 在运用并发集合时,应该留意它们的线程安全性和并发性能,如ConcurrentHashMap是线性安全的,HashMap便对错线性安全的
  • 达观锁,失望锁防止数据库并发.达观锁一般用版别号version操控,失望锁一般用select …for update
  • 假如是单实例的多线程并发处理,一般经过Java锁机制,比方sychronized ,reentrantlock
  • 假如是同一集群的多线程并发处理,可以用Redis分布式锁或许走zookeeper
  • 假如是跨集群的多线程并发处理,则考虑数据库完成的分布式锁。

8. 单元测验标准

  • 测验类的命名,一般以测验的类+Test,如:CalculatorTest.
  • 测验办法的命名,一般以test开头+ 测验的办法,如testAdd.
  • 单测行覆盖率一般要求大于75%.
  • 单测一般要求包含主流程用例、参数边界值等校验用例
  • 单测一般也要求包含中间件拜访超时、回来空、等反常的用例,比方拜访数据库或许Redis反常.
  • 单测用例要求包含并发、防重、幂等等用例.

9. 代码格局标准

杰出的代码格局,可以使代码更简单阅览和了解。下面是一些常见的代码格局化主张:

  • 缩进运用四个空格
  • 代码块运用花括号分隔
  • 每行不超过80个字符
  • 每个办法应该依照特定的顺序排列,例如:类变量、实例变量、结构函数、公共办法、私有办法等。

10. 接口兼容性

代码评定的时分,要要点重视是否考虑到了接口的兼容性.因为许多bug都是因为修正了对外旧接口,但是却不做兼容导致的。要害这个问题多数是比较严重的,或许直接导致体系发版失利的

所以,假如你的需求是在原来接口上修正,尤其这个接口是对外供给服务的话,一定要考虑接口兼容。举个比方吧,比方dubbo接口,原本是只接收A,B参数,现在你加了一个参数C,就可以考虑这样处理:

//老接口
voidoldService(A,B){
//兼容新接口,传个null代替C
newService(A,B,null);
}
//新接口,暂时不能删掉老接口,需求做兼容。
voidnewService(A,B,C){
...
}

12. 程序逻辑是否明晰,主次是否够清楚

代码评定的时分,要重视程序逻辑是否明晰。比方,你的一个注册接口,有参数校验、判别用户是否现已注册、刺进用户记录、发送注册成功通知等功能。

假如你把一切一切功能代码塞到一个办法里面,程序逻辑就不明晰,主次不行清楚,反例如下:

publicResponseregisterUser(StringuserName,Stringpassword,Stringemail){
if(userName==null||StringUtils.isEmpty(userName)){
log.info("用户名不能为空!");
thrownewBizException();
}
if(password==null||password.length()<6){
log.info("暗码长度不能少于6位!");
thrownewBizException();
}
if(email==null||StringUtils.isEmpty(email)||!email.contains("@")){
log.info("邮箱格局不正确!");
thrownewBizException();
}
Responseresponse=newResponse();
UserInfouserInfo=userService.queryUserInfoByUsername();
if(Objects.nonNull(userInfo)){
response.setCode(0);
response.setMsg("注册成功");
returnresponse;
}
UserInfoaddUserInfo=newUserInfo();
addUserInfo.setUserName(userName);
addUserInfo.setPassword(password);
addUserInfo.setEmail(email);
userService.addUserInfo(addUserInfo);
MessageDomessageDo=newMessageDo();
messageDo.setUserName(userName);
messageDo.setEmail(email);
messageDo.setContent("注册成功");
messageService.sendMsg(messageDo);
response.setCode(0);
response.setMsg("注册成功");
returnresponse;
}

其实,以上这块代码,主次不行清楚的点:参数校验就占registerUser办法很大一部分。正例可以划分主次,抽一下小函数,如下:

publicResponseregisterUser(StringuserName,Stringpassword,Stringemail){
//查看参数
checkRegisterParam(userName,password,email);
//查看用户是否现已存在
if(checkUserInfoExist(userName)){
Responseresponse=newResponse();
response.setCode(0);
response.setMsg("注册成功");
returnresponse;
}
//刺进用户
addUser(userName,password,email);
sendMsgOfRegister(userName,email);
//结构注册成功报文
Responseresponse=newResponse();
response.setCode(0);
response.setMsg("注册成功");
returnresponse;
}
privatevoidsendMsgOfRegister(StringuserName,Stringemail){
MessageDomessageDo=newMessageDo();
messageDo.setUserName(userName);
messageDo.setEmail(email);
messageDo.setContent("注册成功");
messageService.sendMsg(messageDo);
}
privatevoidaddUser(StringuserName,Stringpassword,Stringemail){
UserInfoaddUserInfo=newUserInfo();
addUserInfo.setUserName(userName);
addUserInfo.setPassword(password);
addUserInfo.setEmail(email);
userService.addUserInfo(addUserInfo);
}
privatebooleancheckUserInfoExist(StringuserName){
UserInfouserInfo=userService.queryUserInfoByUsername();
if(Objects.nonNull(userInfo)){
returntrue;
}
returnfalse;
}
privatevoidcheckRegisterParam(StringuserName,Stringpassword,Stringemail){
if(userName==null||StringUtils.isEmpty(userName)){
log.info("用户名不能为空!");
thrownewBizException();
}
if(password==null||password.length()<6){
log.info("暗码长度不能少于6位!");
thrownewBizException();
}
if(email==null||StringUtils.isEmpty(email)||!email.contains("@")){
log.info("邮箱格局不正确!");
thrownewBizException();
}
}

13. 安全标准

代码评定,也十分有必要评定代码是否存在安全性问题。比方:

  • 输入校验:应该始终对任何来自外部的输入数据进行校验,以保证它们契合预期而且不会对体系形成损伤。校验应该包含查看数据的类型、大小和格局。
  • 防备SQL注入进犯:在运用SQL查询时,应该始终运用参数化查询或预处理句子,以防止SQL注入进犯。
  • 防备跨站脚本进犯(XSS): 在Web应用程序中,应该始终对输入的HTML、JavaScript和CSS进行校验,并转义特别字符,以防止XSS进犯。
  • 防止灵敏信息走漏: 灵敏信息(如暗码、密钥、会话ID等)应该在传输和存储时进行加密,以防止被未经授权的人拜访。一起,应该防止在日志、调试信息或过错音讯中走漏灵敏信息。
  • 防备跨站恳求假造(CSRF): 应该为一切灵敏操作(如更改暗码、删除数据等)增加CSRF令牌,以防止未经授权的人员履行这些操作。
  • 防备安全缝隙: 应该运用安全性高的算法和协议(如HTTPS、TLS)来维护灵敏数据的传输和存储,并定期对体系进行缝隙扫描和安全性审计。

14. 业务操控标准

  • 一般引荐运用编程式业务,而不是一个注解 @Transactional的声明式业务。因为 @Transactional有许多场景,或许导致业务不生效
  • 业务规模要清晰,数据库操作必须在业务作用规模内,假如对错数据库操作,尽量不要包含在业务内。
  • 不要在业务内进行长途调用(或许导致数据不一致,比方本地成功了,但是长途办法失利了,这时分需求用分布式业务解决方案
  • 业务中防止处理太多数据,一些查询相关的操作,尽量放到业务之外(防止大业务问题)

15. 幂等处理标准

什么是幂等? 计算机科学中,幂等表明一次和屡次恳求某一个资源应该具有同样的副作用,或许说,屡次恳求所产生的影响与一次恳求履行的影响作用相同。

代码评定的时分,要重视接口是否考虑幂等。比方开户接口,屡次恳求过来的时分,需求先查一下该客户是否现已开过户,假如现已开户成功,直接回来开户成功的报文。假如还没开户,就先开户,再回来开户成功的报文。这便是幂等处理。

一般状况有这几种幂等处理方案

  • select+insert+主键/唯一索引冲突
  • 直接insert + 主键/唯一索引冲突
  • 状态机幂等
  • 抽取防重表
  • token令牌
  • 失望锁
  • 达观锁
  • 分布式锁

幂等要求有个唯一标记,比方数据库防重表的一个业务唯一键。一起强调屡次恳求和一次恳求所产生影响是一样的。

16. 中间件留意事项 (数据库,redis)

代码评定的时分,假如用数据库、Redis、RocketMq等的中间件时,咱们需求重视这些中间件的一些留意事项哈。

比方数据库:

  • 重视数据库连接池参数设置、超时参数设置是否合理

  • 防止循环调用数据库操作

  • 假如不分页,查询SQL时,假如条数不清晰,是否加了limit限制限制

  • 数据库的回来是否判空处理

  • 数据库慢SQL是否有监控

  • 表结构更新是否做兼容,存量表数据是否触及兼容问题考虑

  • 索引增加是否合理

  • 是否连表过多等等

比方Redis:

  • Redis的key运用是否标准
  • Redis 反常捕获以及处理逻辑是否合理
  • Redis连接池、超时参数设置是否合理
  • Redis 是否运用了有坑的那些指令,如hgetall、smember
  • 是否或许会存在缓存穿透、缓存雪奔、缓存击穿等问题

17. 留意代码坏滋味问题

了解几个常见的代码坏滋味,大家代码评定的时分,需求重视一些点:

  • 很多重复代码(抽公用办法,规划形式)
  • 办法参数过多(可封装成一个DTO目标)
  • 办法过长(抽小函数)
  • 判别条件太多(优化if…else)
  • 不处理没用的代码(没用的import)
  • 防止过度规划

18. 长途调用

长途调用是代码评定要点重视的一栏,比方:

  • 不要把超时当作失利处理: 长途调用或许会失利,比方网络中断、超时等等。开发者需求留意长途调用回来的过错码,除非是清晰的失利,假如仅仅是超时等问题,不能当作失利处理!而是应该发起查询,承认是否成功,再做处理。
  • 反常处理:长途调用或许会抛出反常,例如因为服务端过错或恳求格局不正确等。因而,开发人员需求保证能够捕获和处理这些反常,以防止体系溃散或数据丢掉。
  • 网络安全:因为长途调用触及网络通讯,因而开发人员需求考虑网络安全的问题,例如数据加密、认证、拜访操控等。尽或许运用安全的协议,例如HTTPS 或 SSL/TLS。
  • 服务质量:长途调用或许会影响体系的性能和可用性。因而,开发人员需求保证服务的质量,例如防止过度运用长途调用、优化数据传输、完成负载均衡等。
  • 版别兼容:因为长途调用触及不同的进程或计算机之间的通讯,因而开发人员需求留意服务端和客户端之间的版别兼容性。尽或许运用相同的接口和数据格局,防止呈现不兼容的状况。
  • 尽量防止for循环长途调用: 尽量防止for循环长途调用,而应该考虑完成了批量功能的接口。