文档规定东方大唐研发中心Java开发规范,包括命名规范、代码注释、代码格式、控制语句、异常处理、SQL语法、文件组织、版本发布、安全规范、其他规范。本文档参考《阿里巴马Java开发手册》,并根据实际情况进行了裁剪,本文档会在工作实际中进行修订。
一、命名规范
1.1 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。 以下是错误示例:
_name / __name / $Object / name_ / name$ / Object$
1.2 代码中的命名严禁使用拼音与英文混合的方式,严禁使用各种无意义的缩写,或无意义的名称。更不允许直接使用中文的方式。 说明: 正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式 也要避免采用。国际通用的名称,可视同英文。
alibaba / taobao / youku / hangzhou
以下是错误示例:
DaZhePromotion
getPingfenByName()
int 循环次数 = 3
1.3 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:(领域模型 的相关命名)DO / BO / DTO / VO 等(后缀)。
MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
以下是错误示例:
macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
1.4 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式。
localValue / getHttpMessage() / inputUserId
1.5 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
MAX_STOCK_COUNT
以下是不推荐示例:
MAX_COUNT
1.6 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
1.7 中括号是数组类型的一部分,数组定义如下:String[] args; 反例:请勿使用 String args[]的方式来定义。
1.8 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。
com.geostar.geoios.util
MessageUtils
1.9 杜绝完全不规范的缩写,避免望文不知义。
AbstractClass “缩写”命名成 AbsClass
condition “缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。
1.10 接口和实现类的命名有两套规则:TODO 整理 对于 Service 和 DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
public class CacheServiceImpl implements CacheService{
}
如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able 的形式)。
public class AbstractTranslator implements Translatable{
}
1.11 枚举类名要带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。
public enum DealStatusEnum {
SUCCESS, UNKOWN_REASON
}
1.12 数据结构命名后面要加后缀,以标识数据结构类型,数组类型的命名必须为复数。自定义类型的变量可以采用本身的名称,把首字母改为小写。
String[] persons;
List<String> personList;
Map<String,String> fieldMap;
Set<Sting> registeredSet;
UserInfo userInfo;
1.13 布尔类型变量命名规则。 临时变量 :使用is、has、can作为前缀表示临时逻辑变量,或使用动词的过去式表结果状态。不得使用flag、status、state等表示含义模糊逻辑变量,变量的命名应能地表达逻辑判断的内容。
boolean hasChildNode,hasMoney;
boolean canWrite,canEat,canFly;
boolean isLoading,isHight,isBoy;
boolean removed,found,done,uploaded;
成员变量 :对于POJO类中的布尔类型成员变量,都不要加is,直接使用去掉is前缀后变量名,否则可能引起序列化错误。
public class Bookstore{
private boolean closed;
private boolean getClosed{
return this.closed;
}
private boolean setClosed(boolean isClosed){
this.closed = isClosed;
}
}
说明: 根据JavaBean的规范,布尔类型(boolean)变量通过IDE自动生成的getter方法为isProperty(),而不是getProperty()。如果成员变量名为isClosed,则通过IDE生成的getter方法是isClosed(),这样会导致序列化框架识判变量名为closed,导致无法正确的赋值。 注意:对于包装的布尔类型(Boolean)变量,通过IDE自动生成的getter方法还是getProperty()。如果成员变量为:Boolean isClosed,则生成的getter方法是getIsClosed(),则不会出现序列化的问题。 1.14 不允许出现任何魔法值(即无意义的常量) 直接出现在代码中, 错误示范
String key = "ID#taobo_"+ tradeID;
cache.put(key,value);
二、代码注释
2.1 类、类属性、类方法的注释必须使用Javadoc规范,使用/**内容*/格式,不得使用 //xxx 方式。 说明: 在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注 释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高 阅读效率。
public int add(int p1, int p2) {
}
2.2 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。 说明: 对子类的实现要求,或者调用注意事项,请一并说明。 2.3 所有的类都必须添加功能、创建者、创建时间、版本信息。
public class Math {
}
2.4 方法内部单行注释,使用//注释。方法内部多行注释 使用/* */注释,注意与代码对齐。
public int getPersonSex(int personId) {
Person person = dao.getPerson(personId);
if (null == person){
return -1;
}
int sex = person.getSex();
return sex;
}
2.5 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
2.6 所有control层做好,swagger 的注解,必须指明这个接口的作用,入参说明,对于有需要特别注意的点使用note注解说明
2.7 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。 说明: 代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后, 就失去了导航的意义。
三、代码格式(推荐)
3.1 大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果 是非空代码块则:
- 左大括号前不换行。
- 左大括号后换行。
- 右大括号前换行。
- 右大括号后还有 else 等代码则不换行;表示终止右大括号后必须换行。
3.2 缩进采用 4 个空格,禁止使用 tab 字符。 说明: 如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。可在 eclipse 中勾选 insert spaces for tabs。
public static void main(String[] args) {
String say = "hello";
if (flag == 1) {
System.out.println("world");
} else {
System.out.println("ok");
}
}
3.3 单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
- 第二行相对第一行缩进 4个空格,从第三行开始,不再继续缩进,参考示例。
- 运算符与下文一起换行。
- 方法调用的点符号与下文一起换行。
- 在多个参数超长,逗号后进行换行。
- 在括号前不要换行。
StringBuffer sb = new StringBuffer();
sb.append("zi").append("xin")...
.append("huang")...
.append("huang")...
.append("huang");
method(args1, args2, args3, ...,
argsX);
以下是错误示例:
StringBuffer sb = new StringBuffer();
sb.append("zi").append("xin")...append
("huang");
method(args1, args2, args3, ...
, argsX);
3.4 IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式, 不要使用 windows 格式。
四、控制语句
4.1 在一个 switch 块内,每个 case 要么通过 break/return等来终止,要么注释说明程 序将继续执行到哪一个 case 为止;在一个switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。
4.2 在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用下面的形式: 错误示例
if (condition) statements;
4.3 尽量少用 else, if-else 的方式可以改写成: 通过return 返回,结束执行逻辑
if(condition){
...
return obj;
}
说明: 如果非得使用 if()…else if()…else…方式表达逻辑,【强制】请勿超过 3 层
4.4 不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
boolean existed = (file.open(fileName, "w") != null)
&& (...) || (...);
if (existed) {
...
}
以下是错误示例:
if ((file.open(fileName, "w") != null) && (...) || (...)) {
...
}
五、异常处理
5.1 不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException /NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
if(obj != null) {
...
}
以下是错误示例:
try {
obj.method();
} catch(NullPointerException e){
...
}
5.2 异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
5.3 对大段代码进行 try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分 异常类型,再做对应的异常处理。
5.4 有 try 块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务。
5.5 finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
5.6 不能在 finally 块中使用 return,finally 块中的 return返回后方法结束执行,不会再执行 try 块中的 return 语句。
5.7 注意防止 NPE(空指针异常) 产生的场景:
- 返回类型为包装数据类型,有可能是 null,返回 int 值时注意判空。 反例:public int f(){ return Integer 对象}; 如果为 null,自动解箱抛 NPE。
- 数据库的查询结果可能为 null。
- 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
- 远程调用返回对象,一律要求进行 NPE 判断。
- 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。
- 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。
5.8 对于系统中的异常,捕获必须进行处理,如果不处理,只是简单的打印日志,直接交给spring框架的全局异常捕获机制即可。
六、数据库规约
6.1 建表原则,表名字段名统一小写,使用下划线分割 6.2 建表类型选择(推荐)
- 整数,当明确知道大小低于127的时候,选用tinyint,其他情形选用int
- 小数,没有特殊情况不要使用小数,使用字符串代替
- 字符串 使用varchar,禁止统一varchar(255),应当按需取最小值,eg:varchar(32) ,字符多时使用text
- 图片等非文字类容,使用blob,
- 日期,使用datatime 存储yyyy-MM-dd HH:mm:ss, 后端接参有统一格式处理,不用再单独处理,前端根据需要进行相关格式化
6.3 不要使用 count(列名)或 count(常量)来替代count(*),count(*)就是 SQL92定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 说明: count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
select count(*) from student;
6.4 count(distinct col)计算该列除NULL之外的不重复数量。 注意: count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。
6.5 当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题。
6.6 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
6.7 不得使用外键与级联,一切外键概念必须在应用层解决。 概念解释: 学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。 如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,则为级联更新。 外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数 据库更新风暴的风险;外键影响数据库的插入速度。
6.8 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。 6.9 禁止使用数据库函数,理由同上
6.10 数据库表结构修改,强制写SQL脚本修改,不得直接使用工具手动修改。 6.11 数据
八、API接口规范
8.1 API命名规范 完整的API格式如下:
[http|https]://[ip]:[port]/[module]</subModule>/[action]
[http|https] http协议或https协议。[ip]和[port] IP地址和端口,如:192.168.100.104:8080、www.geostaryun.com:8088等[module]</subModule> 模块或业务名称,可以包括子模块,子模块为可选,如:book/category、book/sale等。[action] 执行的动作,如:add、update、delete、list、get等。 说明: 命名必须统一使用驼峰命名法。如upperCamelCase、queryCandidate、queryProcessInstance等。
接口中的action 命名规范:
-
新增接口,如新增一个地址、新增一名用户 使用add 名称或以add 为前缀,如:http://192.168.0.1:8088/user/add -
修改接口,如修改用户信息 使用update 名称或以update 为前缀,如:http://192.168.0.1:8088/user/update -
删除接口,如删除用户信息 使用delete 名称或以delete 为前缀,如:http://192.168.0.1:8088/user/delete -
获取接口,如获取用户信息 使用get 名称或以get 为前缀,如:http://192.168.0.1:8088/user/get、http://192.168.0.1:8088/api/v1/user/getAlias -
获取列表接口,如获取一组用户信息 使用list 名称或以list 为前缀,如:http://192.168.0.1:8088/user/list、http://192.168.0.1:8088/api/v1/user/listUser 如果需要不分页获取所有数据,加上all 标识,如:http://192.168.0.1:8088/user/listAll -
关联数据接口,如给用户关联角色、给角色关联权限 使用bind 名称或以bind 为前缀,如:http://192.168.0.1:8088/user/bindRole、http://192.168.0.1:8088/api/v1/role/bindPermission -
移动关联关系接口,如移除用户的管理员角色 使用remove 名称或以remove 为前缀,如:http://192.168.0.1:8088/user/removeRole、http://192.168.0.1:8088/api/v1/role/removePermission -
其他接口 文件上传,使用upload 名称或以upload 为前缀。如:http://192.168.0.1:8088/doc/upload、http://192.168.0.1:8088/api/v1/doc/uploadImage 批量操作,使用batch 名称或以batch 为前缀。如批量上传、批量增加。http://192.168.0.1:8088/api/v1/doc/batchUpload、http://192.168.0.1:8088/user/batchAdd 接口功能,不在上述范围内的请谨慎命名,与上级协商补充。 8.2 请求参数规范 -
请求方式 简单普通数据获取与查询:GET 数据修改、删除:POST 身份验证与敏感数据:POST 参数超过3个的查询:POST 禁止使用put, delete 等请求方式,因为部分框架可能不支持,比如feign框架 -
请求入参 根据实际需要,参数最小化原则。不需要的参数一律不暴露出来,比如新增的时候不需要ID,就不要暴露这个字段给前端。可以定义多个VO对象 参数的定义最好由前端定义。依赖倒置原则,前端更接近业务,有前端制定标准。 -
请求出参 统一使用com.wiilead.it.common.vo.ResponseVO 结构, 相关返回字段推荐由前端定义。 示例:
public ResponseVO saveAssetsType(@RequestBody AssetsAllocationVO assetsAllocationVO) {
try {
JSONObject jsonObject = assetsAllocationService.saveAssetsAllocation(assetsAllocationVO);
if (jsonObject!=null&& !(boolean)(jsonObject.get("flag"))){
return ResponseVO.success("不能操作审批中或已调拨的订单", null);
}
return ResponseVO.success("资产调拨新建成功", null);
} catch (Exception e) {
logger.error("{}",e);
return ResponseVO.fail("资产调拨新建失败", null);
}
}
(7)列表请求特殊规范 pageNo :页数,从1开始。例如:{ “pageNo”: 1 } pageSize : 每页数量。
九、项目结构
十、JAVA技术选择规范
(1)ORM框架采用,mybatis-plus 拒绝使用任何其他第三方ORM框架,说明 :mybatis-plus 是mybatis 的封装,针对单表操作使用简单,保留了所有的mybatis 的功能 尽量使用单表查询,将多表查询拆分。 为了数据库兼容,批量查询,避免循环查询。
(2)API接口,使用统一的ResponseVO对象接收,参考com.wiilead.it.common.vo.ResponseVO.java 该对象有成功和失败的方法封装 使用示例:
try {
Boolean modelExist = modelService.modelExist(bpmnModelVO);
if(modelExist) {
return ResponseVO.fail("模板标识不能重复",null);
}
modelService.modelSave(bpmnModelVO);
return ResponseVO.success("模板信息存储成功",null);
}catch (Exception e){
return ResponseVO.fail("模板信息存储失败",null);
}
(3) 内部Feign 调用,建议直接返回象需要对,避免直接返回Response
推荐使用
@FeignClient(value = ServiceNameConstants.WIILEAD_ALM_SERVICE, fallbackFactory = RemoteBugServiceFallbackFactory.class)
public interface RemoteBugService {
@GetMapping("/bug/fegin/findAllBugVO")
List<BugVO> findAllBugVO(@RequestBody BugVO vo, @RequestHeader(SecurityConstants.FROM) String from);
}
避免使用
@FeignClient(value = ServiceNameConstants.WIILEAD_PM_SERVICE, fallbackFactory = RemoteProjectInfoServiceFallbackFactory.class)
public interface RemoteProjectInfoService {
@GetMapping("/projectInfo/findAllFeign/{strategy}")
ResponseVO findAll(@PathVariable("strategy") String strategy, @RequestHeader(SecurityConstants.FROM) String from);
}
(4)VO 对象是纯净的VO,它只有属性字段。避免依赖于任何第三方jar,推荐Service层返回model 中的对象,controller层返回VO中的对象
(5)所有的表数据带上is_delete 字段,类型char,长度1. 值:0:代表未删除,1代表删除。不要使用魔法值,使用DeleteFlageEnum枚举!所有删除走逻辑删除。office_id, create_by, create_date, update_by, update_date.
(6)删除,不推荐使用级联删除。可以给出提示,有关联关系,让用户自己处理关联数据
(7)父子自关联关系表,根节点避免空值,推荐使用:0 作为根节点的ParentId
十一、 缓存规范
(1) 项目中所有的缓存使用redis 缓存,使用Redistemplate 操作Redis 数据,避免任何其他方式的缓存
(2) 选取合适的Redis 数据结构,避免大量的Redis 数据操作
(3) 查询数据,对于走了Redis 没有找到的,记得要去数据库中查找,不能只走缓存
第三方库
所有的jar依赖都使用wiilead-cloud 父pom文件中的依赖,尤其是依赖版本
附1:常见名称命名规范
关于缩写名词的说明: a. 尽可能多用全称,少用缩写; b. 单个词汇出现的时候,不允许缩写,已固定的缩写(如Id,I是大写)除外; c. 两个或以上单词都可以缩写的时候,只允许一个单词使用缩写,建议第一个单词使用缩写。
序号 | 术语 | 命名 | 缩写 |
---|
1 | 流程 | process | — | 2 | 流程实例 | processInstance | procInst | 3 | 活动(任务)定义 | activity | – | 4 | 活动任务实例 | activityInstance | actInst,taskInst,task | 5 | 待办 | todo/TODO | – | 6 | 已办 | done/DONE | – |
序号 | 术语 | 命名 | 缩写 |
---|
1 | 工作台 | workbench | – | 2 | 文档 | document | – | 3 | 系统 | system | – | 4 | 视图 | view | – | 5 | 流程 | workflow | – | 6 | 菜单 | menu | – | 7 | 角色 | role | – | 8 | 消息 | message | – | 9 | 通知 | notice | – | 10 | 编码 | code | – | 11 | 部门 | department | – |
序号 | 术语 | 命名 | 缩写 |
---|
1 | 战略 | strategy | – | 3 | 项目组合 | projectGroup | – | 4 | 预算 | budget | – | 5 | 合同 | contract | – | 6 | 采购 | purchase | – | 7 | 需求 | requirement | – | 8 | 资源 | resource | – | 9 | 产品 | product | – | 10 | 版本 | version | – | 11 | 迭代 | sprint | – | 12 | 故事 | story | – | 13 | 看板 | board | – | 14 | 团队 | team | – | 15 | 计划 | schedule | – | 16 | 任务 | task | – | 17 | 里程碑 | milestone | – | 18 | 问题 | problem | – | 19 | 风险 | risk | – | 20 | 变更 | change | – | 21 | 报告 | report | – | 22 | 项目 | project | – |
序号 | 术语 | 命名 | 缩写 |
---|
1 | 测试 | test | – | 2 | 测试用例 | testCase | – | 3 | 缺陷 | bug | – |
序号 | 术语 | 命名 | 缩写 |
---|
1 | 新增 | add | – | 2 | 修改 | update | – | 3 | 删除 | delete | – | 4 | 查询 | query | – | 5 | 审批 | approve | – | 6 | 通过 | pass | – | 7 | 驳回 | reject | – | 8 | 撤销 | revoke | – | 9 | 上传 | upload | – | 10 | 下载 | download | – | 11 | 复制 | copy | – | 12 | 拆分 | split | – | 13 | 完成 | complete | – | 14 | 关闭 | close | – | 15 | 变更 | change | – | 16 | 评价 | appraise | – | 17 | 申请 | apply | – | 18 | 保存 | save | – |
TODO:补充所有基本命名
附2:字典编码规范
- 后端所有使用字典的地方使用枚举。每一个数据字典对应一个枚举文件 在bpmn 模块的test目录下存在代码生成工具com.wiilead.it.bpmn.DictUtils
制定对应的字典code,即可自动生成对应的枚举文件 - 步骤一,有代码生产工具,可以自动生成,定义字典的时候定义良好的Code 有助于生成可读性良好的代码
备注:之前的字典码,经历过修改,和规范相对于的字典码已经修改,对应表在SVN上查看 SVN地址:https://120.24.183.170/svn/Wiilead/设计文档/wiilead 产品/数据字典整理.xlsx 注意:字典和枚举的使用场景 字典编码使用XXX三位编码,避免使用1,2,3 1XX-9XX 共9大类
序号 | 状态 | 编码 | 描述 |
---|
1 | 流程审批(所有的审批类) | 6XX | – | 2 | 草稿 | 600 | – | 3 | 审批中 | 610 | – | 4 | 审批完成 | 620 | – | 5 | 驳回 | 630 | – | 6 | 废弃 | 640 | – |
序号 | 状态 | 编码 | 描述 |
---|
1 | 优先级 | 3XX(所有的级别类) | – | 2 | 低 | 300 | – | 3 | 中 | 310 | – | 4 | 高 | 320 | – | 5 | 较高 | 330 | – | 6 | 非常高 | 340 | – |
序号 | 状态 | 编码 | 描述 |
---|
1 | 通过不通过 | 2XX | | 2 | 通过 | 200 | – | 3 | 不通过 | 210 | – |
序号 | 状态 | 编码 | 描述 |
---|
1 | 处理过程 | 5XX | – | 2 | 待处理 | 500 | – | 3 | 处理中 | 510 | – | 4 | 处理完成 | 520 | – | 5 | 关闭 | 530 | – |
附2:代码生成(Eclipse)
在bpmn模块下面,有一个EntityUtils 工具类,用来生成代码。 包括controller VO 等多个文件。
在bpmn模块下面,有一个DictUtils 工具类,可以用来生成字典枚举
附3:代码规范检查插件(Eclipse)
(1)开发环境
- Eclipse Juno+
- maven3.+
- JDK 1.7+
(2)安装插件
(3)使用插件
- 在工程目录中,右键点击目录或Java文件,在弹出的菜单中选择Alibaba Coding Guidelines Analyse,或者按快捷键
Ctrl +Alt +Shift +J - 在代码编辑器窗口,可以查看到所有不规范代码的提示。
|