Blame view

Java编程规范.md 8.17 KB
xjipeng authored
1 2 3 4



xjipeng authored
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
## Java Bean规范

- 模型要严格分离,DO是数据库模型,BO是服务层模型,VO是Gateway对外暴露的模型,服务之间、gateway和服务之间,传输的对象是BO,Gateway对外暴露的是VO,访问数据库的事DO
- bean需要实现tostring方法。 如果加入到hashmap、hashtable、set之类容器的bean,需要实现hashcode和equals方法
- 如非必要,使用int,long,double、float代替包装类Integer、Long、Double、Float   (自动生成的DO除外)
- 所有的BO、VO、DO,必须向前兼容,只能添加属性,不能废弃原字段,原字段必须支持并正确赋值。通过@Deprated注解标注,并明确规划在未来的某个版本删除
- 所有的VO类型必须为全字符串类型(新增接口和字段)

## 数据库操作

- Dao方法命名规范,必须以select、delete、insert、update开始
- Dao层接口的命名由I + 表名 + Dao构成,例如:IUserBaseDao.java, 对应表user_base;
- 除非必要,禁止在sql中做运算功能
- 禁止任何跨库关联查询
- 尽可能把数据库当KV存储使用
- 除非必要,禁止使用 join 查询
- 禁止出现一个数据库表创建多个DAO,一个表只允许在一个工程中使用
- 从数据库中获取非关键信息失败时,使用默认值
- 禁止每次查询静态配置表,应在进程内缓存
- 禁止for、while循环中存在数据库操作,以批量替代
- Mybatis返回一个对象时,sql语句中默认添加limit 1


## 缓存Redis使用

- 数据持久化使用mysql,不依赖redis持久化
- redis的操作归集到一个类中,禁止修改缓存,需要修改的时候,直接delete key(单个key存在高并发场景的计数器除外)
- 对缓存更新不用采取数据库和缓存同步更新,首先更新数据库,再删除缓存;后续在使用时,再次触发缓存;禁止修改redis中数据
- 进程内的缓存,使用yoho-core框架中的LocalCache,禁止自行创建全局变量和更新线程
- 浏览类业务,比如资源位、商品等,必须支持二级缓存(服务灾备)
- 向Redis写数据时,必需设置有效期
- 使用前台的Redis时,必需使用yoho-core框架中的YHRedisTemplate、YHValueOperation等,禁止使用其他方式操作redis。使用外部redis,参考core中,自己定义template
- 所有缓存必需提供清理接口,由写入的服务提供
- 禁止for、while循环中操作缓存,以批量替代
- 尽可能使用一个key完成多个维度数据的缓存,比如使用ZSet、Hashes
- 缓存中默认保存字符串,对象转换为json后再存储到redis中,禁止采用其他方式序列化对象
- 有效期超过90天,必需使用longexpire
- 所有redis操作失败,必须支持回源数据库
- 禁止使用集合的并、交,模糊key,例如keys *


## 消息队列

- 消费者处理时保证幂等性,支持重复处理同样的消息
- 消费者对于处理失败的消息,并且不能丢弃时,需抛出异常; 如果消息可以丢弃,则吃掉所有异常;
- 对于非实时感知结果的操作,尽量使用MQ,由后台进程处理;
- 对于MQ的使用,必须经过开发经理确认,才可以使用; 禁止私自使用MQ;
- 如果无性能瓶颈的场景,禁止使用MQ;如果必要使用,需向团队报备;

## 线程使用

- 禁止在Gateway和服务中自行创建线程,需要异步线程处理的任务,由后台进程完成
- 除非必要,禁止使用ThreadLocal;每个线程使用时,必须首先remove
- 创建线程、线程池必须要添加名称;
- 创建大量线程(线程数量>5)、循环中创建线程,需要和模块负责人确认

## 服务调用

- 请求外部系统时,统一使用service.call,比如erp、search、短信sp等
- 多次服务调用时,如果没有必然联系,使用异步并发调用
- 非关键请求时,传入默认值,比如获取用户vip信息、获取促销信息失败,用户仍然可以下单、查看购物车
- 服务调用选择最小化信息的接口,提高响应速度和降低负载
- 禁止在for、while循环中调用服务,均以批量接口替代
- 一次接口调用处理过程,对同一个服务,只能消费一次,需要聚合请求

## 服务提供者

- 服务处理异常流程,返回ServiceException,并定义服务错误码和接口错误码
- 禁止使用true、false做为返回值代替各种含义,比如成功、失败等,必须定义明确的返回值(失败可用ServiceException代替)
- 避免提供大而全的服务接口,为不同场景提供不同粒度的服务

## 日志打印

- Gateway 和 服务的所有 restapi的入口和返回、关键路径,都要打印info级别日志。日志内容包括请求参数,关键响应结果(对于过长的响应结果,只打印关键信息)
- 处理非自定义业务Exception(例如网络异常、数据库异常)的时候,需要打印WARN异常堆栈
- 关键业务,例如注册、支付、订单等需要打印专门的日志文件
- 如非必要,禁止在循环中打印日志
- Gateway所有接口增加trace注解,记录关键请求参数
- 日志使用英文清晰描述场景,禁止打印中文
- 用户关键信息打印时,比如(密码、电话号码)在日志中打印时需要模糊化处理
- 非关键路径的日志,打印debug级别日志

## 性能指标

- 一般情况下,服务接口延时不超过50ms
- Gateway对外提供接口,延时不能超过200ms

## 工程依赖

- 任何业务工程、子工程,不能存在互相依赖;
- 业务工程之间公用部分,提取工程发布到maven私服;比如搜索各工程间共用dal
- 所有工程、子工程的POM文件中,禁止出现依赖版本号,统一在yoho-parent中管理
- 外部依赖的配置(例如外部redis地址、外部restful接口地址)添加到global配置中,自己工程的配置加入auto-conf中。


## 基础编程

- 数字和字符串常量,统一定义final变量,命名规则全大写
- 禁止if、else、try、catch多层嵌套,在核心业务逻辑处理前,优先处理异常逻辑
- 禁止在Gateway、服务中定义全局变量
- 如果获取非核心配置、信息失败,采用默认配置;比如,获取vip信息失败,导致无法下单
- 类、public方法必须注释用途、参数含义、返回值含义,特别是restapi、service所有public必须明确注释
- 关键业务的每个路径、普通业务的关键路径必须详细注释
- 每个方法的实际代码不超过30行(排除注释、换行等)

## 金额计算

- 如果首先计算商品本身价格,则丢弃精度,比如原价 81.1元,85折后68.935元,则商品价格取68.93元,让利用户
- 如果首先计算优惠价格,则向上取, 比如原价 81.1元,满减计算时优惠 12.344元,则优惠金额取值为 12.35元,让利用户
- 每次加减运算使用yoho-core中的YHMath


## Git
- 只能使用 SourceTree 作为客户端工具。 禁止使用任何其他工具。

## 命名规范
xjipeng authored
121
```
xjipeng authored
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
     Controller类是以英文单词组成;由模块名 + Controller构成,例如:UserController.java;
     Controller层方法的命名:同Service层方法的命名(绝大多数情况我们的Controller作为Service的访问跳板)

     Service层接口的命名由I + 模块名 + Service构成,例如:IUserService.java;
     Service层实现的命名由 模块名 + ServiceImpl构 成,例如:UserServiceImpl.java
     Service层方法的命名:以 操作动词 + ValueObject名
         创建前缀:create,比如:createUser(User user);
         修改前缀:update,比如:updateUser(User user);
         删除前缀:delete,比如:deleteUser(User user);
         查询前缀:find,比如:findUserByGroupId(Long groupId);
         统计前缀:stat,比如:statUser();

     Dao层接口的命名由I + 表名 + Dao构成,例如:IUserDao.java;
     Dao层方法的命名:以 操作动词 + ValueObject名
         创建前缀:insert
         修改前缀:update
         查询前缀:select
xjipeng authored
139
```
xjipeng authored
140 141 142 143 144 145 146 147 148 149 150 151 152 153