Java研发规范

文章目录

  • 一、code规范
  • 二、需求跟进实践总结
        • **需求分析**
        • **设计**
        • **开发**
  • 三、幂等&一致性
  • 四、代码规范
  • 五、项目流程
      • 5.1 详设汇总
      • 5.2 排期
      • 5.3 数据组影响
      • 5.4 全流程SOP预案
      • 5.5 每日测试进度汇总
      • 5.6 灰度方案
      • 5.7 上线计划
      • 5.8 风险评估

一、code规范

  • 强制】POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误

    1. 反例:定义为基本数据类型Boolean isDeleted的属性,它的方法也是isDeleted(),RPC框架在反向解析的时候,“误以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常
  • 强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中(对for循环中的循环变量(i或j等)的赋值,暂且不认为是魔法值)

  • 强制】不允许任何魔法值(即未经预先定义的常量)直接出现在代码中(对for循环中的循环变量(i或j等)的赋值,暂且不认为是魔法值)

  • **【强制】**Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals(说明:推荐使用 java.util.Objects#equals(JDK7 引入的工具类))

    1. 正例:“test”.equals(object)
    2. 反例:object.equals(“test”)
  • **【强制】**定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值

    1. 反例:POJO 类的 gmtCreate 默认值为 new Date(),但是这个属性在数据提取时并没有置入具 体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间
  • **【强制】**使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是 list.size()(说明:使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配内存空间,并返回新数组地址;如果数组元素个数大于实际所需,下标为[ list.size() ] 的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致)

    正例:  List<String> list = new ArrayList<String>(2); 
    
    ​          list.add("guan"); list.add("bao");String[] array = new String[list.size()]; 
    
    ​          array = list.toArray(array); 
    
  • 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常(说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组)

    String[] str = new String[] { “you”, “wu” }; List list = Arrays.asList(str);

    第一种情况:list.add(“yangguanbao”); 运行时异常

    第二种情况:str[0] = “gujin”; 那么list.get(0)也会随之修改

  • **【强制】**不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁

  1. 正例:List list = new ArrayList<>(); list.add(“1”);list.add(“2”);

    Iterator iterator = list.iterator();

    while (iterator.hasNext()) { String item = iterator.next();if (删除元素的条件) { iterator.remove(); } }

  2. 反例:for (String item : list) { if (“1”.equals(item)) {list.remove(item);} }




- 【**强制**】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

说明:Executors 返回的线程池对象的弊端如下: 

\1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM

\2) CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM

- 在高并发场景中,避免使用”等于”判断作为中断或退出的条件(说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间 判断条件来代替)

1. 反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止

- **【强制】**不要在 finally 块中使用 return(说明:finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句) 

- **【强制】**类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用 // xxx方式(说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注 释;在 IDE 中,工程调用方法时,不进入方法即可悬浮 示方法、参数、返回值的意义, 提高阅读效率)

- **【强制】**所有的类都必须添加创建者和创建日期 

- 【推荐】可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。如非必要,请不要在此场景打出 error 级别,避免频繁报警(说明:注意日志输出的级别,error 级别只记录系统逻辑出错、异常或者重要的错误信息)

- 【推荐】尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用 中文 述即可,否则容易产生歧义。

- 禁止事务内有rpc 调用

- 禁止事务传播、嵌套

- insert每次批量插入的行限制在200

```java
@Transactional(rollbackFor = Exception.class)
  public void updateOrderCount(Req request) {
      xxxMapper.batchUpdateStatus(request);
      xxxMapper.batchUpdate(request);
  }
  • 数字类型的计算原则

【强制】数字运算表达式,因为先进行等式右边的运算,再赋值给等式左边的变量,所以等式两边的类型要一致

double d = 24/7;  //结果是3.0
double d =  (double)24/7; //结果是正确的3.42857

intint相乘,哪怕被赋值给long,仍然会溢出。需要强制将乘数的一方转换为longlong l = Integer.MAX_VALUE * 2; // 结果是溢出的-2
long l = Integer.MAX_VALUE * 2L; //结果是正确的4294967294
  • 索引

随机IO:随机IO是指读写操作时间连续,但访问地址不连续,随机分布在磁盘的地址空间中。

  • 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。举个例子,假如说班级学生表有no(学号)、name(姓名)、age(年龄)、address(地址)四个字段,那么对于index_name_age就是可以直接简化为index_name,因为重名的很少,在相同name下的学生age不同的不会很多,所以age建立索引意义不大。
  • 批量插入返回主键,涉及到跨表时,useGeneratedKeys切记指定false

二、需求跟进实践总结

需求分析

对需求方提出的所有需求,进行详细的分析。这个阶段一般需要和客户反复确认,以保证能充分理解客户需求。最终会形成需求分析文档。

  1. 扩大需求理解范围:在产品方案中除了本系统的需求,往上下游需求中去发散,并且了解需求实际场景。
  2. 疑点及时确认:得到信息输入后对于设计疑点提出质疑,及时归纳整理。
  3. 异常处理流程:在下游接口异常或者根据业务流程产生某种异常时,展示给前端的逻辑需要跟产品确认。
  4. 整体需求串联:基于对prd的理解串联本系统跟上下游的功能逻辑,联合技术分工看整体功能是否是联通的,如有遗漏及时跟产品或RD进行确认。
  5. 需求场景延伸:了解需求的真实场景,辅助自己做出判断,以及对功能的未来走向做提前规划。
  • 设计

根据需求分析的结果,对整个软件系统进行抽象和设计,如系统框架设计,数据库设计等等。最后会形成架构设计文档。

  1. 系统边界确认:确认本系统的边界,确保在需求设计和开发的过程中,系统边界不被穿透。
  2. 全链路功能串联:需求对应的全链路各个服务提供的功能串联起来,认领相应缺少部分功能,产出系统逻辑图。
  3. 组内需求拆分:在需求较大或确认内容较多时,拆分需求多线程确认各自的逻辑。需要精确对应责任人,在ones上创建相应的设计任务,明确开始、截止时间,保证任务状态及时变更。
  4. 整体需求串联:基于对prd的理解串联本系统跟上下游的功能逻辑,联合技术分工看整体功能是否是联通的,如有遗漏及时跟产品或RD进行确认。
  5. 边界内技术方案设计:各自对拆分的需求进行确认和设计,产出流程图。
  6. 边界线技术设想:对交互的边界有一定的要求和大概的设想。【SLA、异常处理】
  7. 未来规划:基于收集到的业务需求,基于对业务的认知,提前规划项目未来的发展方向。
  8. 技术方案组内对齐:在进行全链路技术方案对齐之前,先保证方案在小组、大组内确认通过,避免反复确认,明确项目边界。
  9. 全链路技术方案对齐:本系统的技术方案,在组内确认通过后跟直接交互的上下游确认是否通顺。

特别注意:

  • 在整体对外的设计中确保系统边界不会被穿透。
  • 设计过程中对可预估的风险进行提前准备,对于内部无法处理的风险push相关人员解决。
开发

将架构设计和界面设计的结果转换成计算机能运行的程序代码。

  1. 任务拆分:对应技术方案拆分任务,需要精确对应责任人,在ones上创建相应的设计任务,明确开始、截止时间,保证任务状态及时变更。
  2. buffer预留:通常需求采用倒排的形式,但是也要对工作量有个大概的预估,比对时间是否存在风险,提前处理。
  3. 代码异常处理逻辑:确保整体代码异常处理逻辑准确性。
  4. 需求异常处理逻辑:在需求开发过程中涉及到的异常判断要慎重处理,是要阻断流程还是要做兼容处理,要有明确的确认结果及确认人。确认需求中的异常处理逻辑和实现的功能异常处理逻辑一致。
  5. 优先级开发:涉及到具体开发,一次需求通常涉及的链路较长,优先保证主体流程的进度,对需求中的工作进行大概排序,优先级较低的可以多线程开发。
  6. 值班增效:考虑到后续会有服务值班以及联调排查问题的需求,涉及到上下游访问的地方需要添加相应的cat及日志打印。针对值班效率提升的问题可以做一些提前的规划。
  7. CodeReview:对于新人或者新系统需每日CR保证开发规范,正常项目开发中每个里程碑必须进行CR。
  8. 加速度保障设计和建设:基于了解到的需求,在设计和开发本次需求过程中减少下次需求的改动量或者提前做些准备,给下期需求形成加速度。
  9. 稳定性保障设计和建设:需求进行过程中考虑稳定性及扩展性问题。
  10. 需求互备:组内人员除对各自需求明确以外,需要了解别人的需求,防止突发情况可以快速顶上。
  11. 代码重读:在精力空余的情况下,可以对本期代码重新阅读下。

三、幂等&一致性

  • 生产者:重复@Retryable发送MQ,同步或异步阻塞拿到结果

  • 消费者:消费逻辑不成功时,通过RECONSUME_LATER再次重试消费MQ,直到设定的最大重试次数时,不管成功与否,MQ的状态都为CONSUME_SUCCESS

四、代码规范

  • 禁止return null,使用 Optional

  • 接口必须抛出 TException

    MTthrift注解方式声明rpc接口时,TException必须要声明(否则调用方会看到UndeclaredThrowableException,而隐藏了真实的异常信息)。TException代表的是网络异常、服务注册异常等非业务异常;不可以在业务异常的情况下抛出TException。调用方应该对TException类异常做单独处理:譬如重试机制;譬如异常信息的独立封装。

  • 接口不对外显示抛出业务异常

    thrift接口定义不对外显示抛出Exception,业务层可以抛出BizXXXException,但异常必须有service层统一处理,不对外抛出,而是将异常转成对应的errorCode返回。

五、项目流程

5.1 详设汇总

1、方向、rd、详细设计、技术方案预计完成时间、是否完成评审、备注

2、技术方案评审todo项

5.2 排期

1、方向、rd(前后端)、研发排期(含PD)、联调时间、QA测试时间

2、是否提测完成

5.3 数据组影响

是否有变更需要周知数据组

5.4 全流程SOP预案

降级、灰度

5.5 每日测试进度汇总

5.6 灰度方案

方向、rd、灰度方案(有的方向不需要)、灰度顺序、回滚方案、备注

5.7 上线计划

方向、rd、上线checklist、上线时间、是否完成上线、备注(服务1依赖服务2先上线等)

5.8 风险评估

项目名称、项目负责人(产品主R、技术主R)、项目背景、项目收益、计划上线时间、灰度节奏(灰度方案)

风险类别(业务风险-不可用、稳定性风险)、风险内容、风险评估人员、风险预案措施

审批人(灰度放量等)

你可能感兴趣的:(java,研发规范,项目流程)