前言

《易族智汇Java开发规范手册》是公司技术团队的集体经验总结,现代软件行业的高速 发展对开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点 也会影响到软件的最终交付质量。比如:数据库的表结构和索引设计缺陷可能带来软 件上的架构缺陷或性能风险;工程结构混乱导致后续维护艰难;没有鉴权的漏洞代码 被黑客攻击等等。所以本手册以 Java 开发者为中心视角,划分为编程规约、异常日志 规约、MySQL 规约、工程规约、安全规约五大块,再根据内容特征,细分成若干二级子 目录。根据约束力强弱及故障敏感性,规约依次分为强制、推荐、参考三大类。对于 规约条目的延伸信息中,“说明”对内容做了引申和解释;“正例” 倡什么样的编 码和实现方式;“反例”说明需要 防的雷区,以及真实的错误案例。
  本手册的愿景是码出质量、码出高效。代码的字里行间流淌的是软件生命中的血液,质量的 升是尽可能少踩坑,杜绝踩重复的坑,切实 升质量意识。另外,现代软件架构都需要协同开发完成,高效协作即降低协同成本, 升沟通效率,所谓无规矩不成方圆,无规范不能协作。众所周知,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全。试想如果没有限速,没有红绿灯,谁还敢上路行驶。对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事, 升协作效率。

一、编程规约

(一)命名风格

1.【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

反例:name / \_name / $Object / name_ / name$ / Object$

2.【强制】 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3
 正例: alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。

3.【强制】类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO /DTO / VO / AO

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
 反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
  BO 业务对象,封装对象、复杂对象 ,里面可能包含多个类;
  DTO 传输对象,前端调用时传输 ;
  VO 表现对象,前端界面展示。

4.【强制】方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格,必须遵从 驼峰形式。

正例: localValue / getHttpMessage() / inputUserId

5.【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。

正例:MAX_STOCK_COUNT
 反例:MAX_COUNT

6.【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称
 开始,以 Test 结尾。

7.【强制】中括号是数组类型的一部分,数组定义如下:String[] args;

反例:使用 String args[]的方式来定义。

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

反例:定义为基本数据类型 Boolean isDeleted;的属性,它的方法也是 isDeleted(),RPC框架在反向解析的
 时候,“以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

9.【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果
 有复数含义,类名可以使用复数形式。

正例: 应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考 spring 的框架结构)

10.【强制】杜绝完全不规范的缩写,避免望文不知义。

反例: AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类 随意缩写严重降低了代码的可阅读性。

11.【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类与接口用I的前缀区别。

正例:MemberManager 实现 IMemberManager 接口。

12.【强制】组件类,全部以Component结尾

正例:MemberComponent。

13.【强制】Controller类中的方法,下面提供通用的方法名

方法名 解释
saveAdd 保存添加
saveEdit 保存修改
delete 删除
listJson 列表的json

14.【强制】Controller类api路径格式
  1)类上使用@RestController注解
  2)方法上分别使用@GetMapping,@PostMapping,@DeleteMapping注解
  比如对会员进行增删该查

方法 正例 反例
增 @PostMapping("/member") @PostMapping("/save-member")
删 @DeleteMapping("/member/{member_id}") @DeleteMapping("/delete-member")
改 @PostMapping("/member/{member_id}") @PostMapping("/update-member")
查 @GetMapping("/member") @GetMapping("/get-member")

3)api组成
  业务模块关键字/权限关键字/业务关键字..

  • 权限关键字

会员 member 卖家 seller 管理员 admin

  • 业务模块关键字

店铺 shop 商品 goods 会员 member 促销 promotion 订单 order 系统 system 楼层 cms

15.【参考】各层命名规约
  A)Service/DAO层方法命名规约
   1)获取单个对象的方法用get做前缀。
   2)获取多个对象的方法用list做前缀。
   3)获取统计值的方法用count做前缀。
   4)插入的方法用save(推荐)或insert做前缀。
   5)删除的方法用remove(推荐)或delete做前缀。
   6)修改的方法用update做前缀。
  B) 领域模型命名规约
   1) 数据对象:xxxDO,xxx 即为数据表名。
   2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
   3) 展示对象:xxxVO,xxx 一般为网页名称。
   4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

16.【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
 正例:枚举名字为 ProcessStatusEnum 的成员名称:SUCCESS / UNKOWN_REASON。

(二)常量定义

  1. 【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中。 反例:String key = "Id#taobao_" + tradeId;
    cache.put(key, value);

  2. 【强制】long 或者 Long 初始赋值时,使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。 说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?

  3. 【推荐】不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。 说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
    正例:缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下。

(三)代码格式

  1. 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是非空代码块则:
      1) 左大括号前不换行。
      2) 左大括号后换行。
      3) 右大括号前换行。
      4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。

  2. 【强制】 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格。详见第 5 条下方正例提示。 反例:if (空格 a == b 空格)

  3. 【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。

  4. 【强制】任何二目、三目运算符的左右两边都需要加一个空格。
    说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。

  5. 【强制】采用 4 个空格缩进,禁止使用 tab 字符。
    说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时,请勿勾选 Use
    tab character;而在 eclipse 中,必须勾选 insert spaces for tabs。
    正例: (涉及 1-5 点)

`

  1. 【强制】注释的双斜线与注释内容之间有且仅有一个空格。 正例:// 注释内容,注意在//和注释内容之间有一个空格。
  2. 【强制】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:  1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
     2) 运算符与下文一起换行。
     3) 方法调用的点符号与下文一起换行。
     4) 方法调用时,多个参数,需要换行时,在逗号后进行。
     5) 在括号前不要换行,见反例。

  1. 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。 正例:下例中实参的"a",后边必须要有一个空格。 method("a", "b", "c");

(四)OOP 规约

  1. 【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。

  2. 【强制】所有的覆写方法,必须加@Override 注解。 说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。

  3. 【强制】相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object。 说明:可变参数必须放置在参数列表的最后。(提倡同学们尽量不用可变参数编程) 正例:public User getUsers(String type, Integer... ids) {...}

  4. 【强制】外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。

  5. 【强制】不能使用过时的类或方法。 说明:java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时,应该使用双参数 decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。

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

  7. 【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 说明:对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。

  8. 关于基本数据类型与包装数据类型的使用标准如下: 1)【强制】所有的 POJO 类属性必须使用包装数据类型。 2)【强制】RPC 方法的返回值和参数必须使用包装数据类型。 3)【推荐】所有的局部变量使用基本数据类型。 说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE 问题,或者入库检查,都由使用者来保证。 正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 反例:比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

  9. 【强制】定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。 反例:POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。

  10. 【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。 说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。

(五)控制语句

  1. 【强制】在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。

  2. 【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:if (condition) statements;

  3. 【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成: if (condition) {   ...   return obj; } // 接着写 else 的业务逻辑代码; 说明:如果非得使用 if()...else if()...else...方式表达逻辑,【强制】避免后续代码维护困难,请勿超过 3 层。

    (六)注释规约

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

  5. 【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 说明:对子类的实现要求,或者调用注意事项,请一并说明。

  6. 【强制】所有的类都必须添加创建者和创建日期。

  7. 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。

  8. 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。

  9. 【推荐】与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。 反例:“TCP 连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。

  10. 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。 说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后, 就失去了导航的意义。

    (七)其他

  11. 【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。 说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);

  12. 【强制】velocity 调用 POJO 类的属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的getXxx(),如果是 boolean 基本数据类型变量(boolean 命名不需要加 is前缀),会自动调用 isXxx()方法。 说明:注意如果是 Boolean 包装类对象,优先调用 getXxx()的方法。

  13. 【强制】后台输送给页面的变量必须加$!{var}——中间的感叹号。 说明:如果 var=null 或者不存在,那么${var}会直接显示在页面上。

  14. 【强制】注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

  15. 【强制】获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime(); 说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中, 针对统计时间等场景,推荐使用 Instant 类。

二、异常日志

(一)异常处理

  1. 【强制】Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,比如:IndexOutOfBoundsException,NullPointerException 等等。 说明:无法通过预检查的异常除外,如在解析一个外部传来的字符串形式数字时,通过 catch NumberFormatException 来实现。 正例:if (obj != null) {...} 反例:try { obj.method() } catch (NullPointerException e) {...}

  2. 【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。

  3. 【强制】对大段代码进行 try-catch,这是不负责任的表现。catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

  4. 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。

  5. 【强制】有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。

  6. 【强制】finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。 说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。

  7. 【强制】不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。

  8. 【推荐】方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。 说明:本手册明确防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败、序列化失败、运行时异常等场景返回null 的情况。

  9. 【推荐】防止 NPE,是程序员的基本修养,注意 NPE 产生的场景: 1)返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。 反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。 2) 数据库的查询结果可能为 null。 3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。 4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。 5) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。 6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。 正例:使用 JDK8 的 Optional 类来防止 NPE 问题

  10. 【推荐】定义时区分 unchecked / checked 异常,避免直接抛出 new RuntimeException(),更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定义过的自定义异常,如:DAOException / ServiceException 等。

  11. 【参考】在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http/api 开放接口必须使用“错误码”;而应用内部推荐异常抛出;跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、“错误码”、“错误简短信息”。 说明:关于 RPC 方法返回方式使用 Result 方式的理由: 1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。 2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输 的性能损耗也是问题。

  12. 【参考】避免出现重复的代码(Don’t Repeat Yourself),即 DRY 原则。 说明:随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。 正例:一个类中有多个 public 方法,都需要进行数行相同的参数校验操作,这个时候请抽取: private boolean checkParam(DTO dto) {...}

(二)日志规约

1.【强制】应用中需要记录日志,使用日志系统Log4j中的 API,记录日志。 Import org.apache.log4j.Logger; private final Logger logger=Logger.getLogger(getClass());

三、安全规约

  1. 【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。 说明:防止没有做水平权限校验就可随意访问、修改、删除别人的数据,比如查看他人的私信内容、修改他人的订单。

  2. 【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。 说明:查看个人手机号码会显示成:158**9119,隐藏中间 4 位,防止隐私泄露。

  3. 【强制】用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库。

  4. 【强制】用户请求传入的任何参数必须做有效性验证。 说明:忽略参数校验可能导致: 1)page size 过大导致内存溢出 2) 恶意 order by 导致数据库慢查询 3) 任意重定向 4) SQL 注入 5) 反序列化注入 6) 正则输入源串拒绝服务 ReDoS 说明:Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题, 但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的结果。

  5. 【强制】禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。

  6. 【强制】表单、AJAX 提交必须执行 CSRF 安全过滤。 说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在CSRF 漏洞的应用/网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户不知情情况下对数据库中用户参数进行相应修改。

  7. 【强制】在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制,如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。 说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。

results matching ""

    No results matching ""