研发二部JAVA后台开发规约(参考阿里JAVA代码规范)

主要内容

  1. 工程规约
  2. 编码规约
  3. 异常处理日志规约
  4. MySQL规约
  5. 安全规约

工程规约

应用分层:


研发二部JAVA后台开发规约(参考阿里JAVA代码规范)_第1张图片
应用分层图.png

工程分层如上图所示,上层可直接调用下层,特殊情况下可跨层调用最多跨一层调用。切不可各个层之间随意调用。不同的层级处于不同的程序包。

  • 页面显示层:各个端的模板渲染并执行显示层,当前系统主要包含Beetl模板,移动端h5渲染,js渲染。直接调用请求处理层获取数据。
  • 开放接口层:封装Service层接口暴露RPC接口(暂时没有用到),通过Web封装http接口(常用),以及网关控制层等。
  • 请求控制层:主要对访问请求进行转发控制,主要做参数校验,简单不重复业务逻辑处理(代码不超过10行)。
  • 业务逻辑层:处理具体业务逻辑服务层,提供给请求控制层使用可直接调用数据持久层和通用处理层。
  • 通用业务处理层: 对复杂的业务,或者需要调用外部接口以及第三方平台的业务逻辑进行封装。
  • 数据持久层:主要为对数据库进行访问,与MySQL等数据库进行交互。

Tips:

因数据持久层(Dao层)异常类型过来,建议直接使用Exception直接捕获,然后重新Throws DaoException(自定义的异常)到Service层统一处理记录日志。Web层异常必须处理禁止继续往上抛出异常。

分层领域模型规约:

  • DO(Data Object)与数据库表结构一一对应,通过DAO层向上传输数据。
  • VO(View Object)显示层对象,通常是Web层向外提供的对象。
  • QO(Query Object)数据查询对象,各层接受上层的查询请求的查询对象。参数不超过两个直接使用普通类型,超过则必须封装成查询对象,禁止使用Map类型。
    以上三种为常用对象,但工作中遇到特定的问题可以进行变通引入其它模型对象。
    工作中会涉及到各个对象间的转化,这里给大家推荐使永Cglib里面的BeanCopier类

三方库规约:

  • 必须使用maven引入第三方库
  • 引入前必须大家一起确认使用版本
  • 不得使用SNAPSHOT(不稳的版本)
  • 使用同一版本变量
  • 尽量使用不用配置的第三方库
  • 依赖冲突需要解决

编码规约

编码规约这一块比较枯燥,但是请务必通读,并严格执行。
编码规约包含如下几个方面的内容:

  1. 命名规约
  2. 常量定义规约
  3. 代码格式规约
  4. OOP(面向对象)规约
  5. 集合处理规约
  6. 并发处理规约
  7. 控制语句规约
  8. 注释规约
  9. 方法规约
  10. 其它规约

命名规约

  • 代码中命名禁止使用特殊符号开始和结束。
    反例:name name $name$
  • 代码中严禁使用中文,拼音,拼音与英文混用
    反例:chedai gerendai
  • 类名必须使用大写字母开头的驼峰命名法,以下情形例外(领域模型相关命名)DO / VO / QO
    反例: user XMLService TAPromotion
  • 方法名、参数名、成员变量、局部变量、必须使用小写字母开头的驼峰命名法。
    正例: userName password
  • 常量名全部大写单词间用下划线隔开,不必担心名字过长但求语义表达清晰
    正例:MAX_STOCK_COUNT
    反例:MAX_COUNT
  • 抽象类名使用Abstract开头,基类使用Base开头,异常类命名以Exception结尾,测试类命名以要测试的类开头Test结尾。
    正例: UserServiceTest
  • 数组定义统一使用中括号前置的方式: String[] args
  • POJO(简单的Java对象不包含业务方法)类中布尔值变量不要加is开头,因为这个会引发部分框架序列化错误。
  • 包名必须统一使用小写,并使用单数形式。
    正例:com.kaisafax.message.util
  • 切记不可使用不常见的英文缩写
    反例: AbstractClass 写成 AbsClass
  • 如果使用设计模式可将设计模式名字放到最后
    正例: OrderFactory UserBuilder
  • 接口类的方法和属性不加任何修饰符号(public也不需要)这样代码更整洁,接口中属性默认是public static final修饰,方法默认是public abstract。可加上有效的Javadoc注释,尽量少定义变量,如果一定要定义变量必须和接口方法相关,且是整个应用的基础常量。
    正例: 接口方法名:void save();接口常量:String SAVE_SUCCESS = “success”;
  • 接口和实现类命名,对于Service和DAO类,基于SOA理念暴露的服务一定时接口,内部的实现类使用Impl后缀与接口区别。
    正例:UserService 实现接口为:UserServiceImpl
  • 枚举类建议使用Enum后缀,其成员名称使用全大写并用下划线隔开
    正例: DealStatusEnum 成员名称: SUCCESS,ERROR,UNKOWN_REASON
  • 各个层命名
    Service和DAO层 以Service和Dao结尾
    获取单个对象方法用get为前缀
    获取多个对象使用list为前缀
    获取统计值用count为前缀
    插入使用insert为前缀
    删除使用delete为前缀
    更新使用update为前缀
  • 领域模型命名规约
    数据对象:表名大写
    展示对象: xxxxVO xxx为一般可为展示网页的名称

常量定义规约

  • long和Long类型变量初始化值的时候必须使用L结尾。
  • 常量复用层次:跨应用共享常量[较少使用],应用内常量[由应用负责人在modules中的constant目录编写并形成文档分发],子工程共享常量[由子工程负责人在当前子工程constant目录下编写并形成文档分发],包内共享常量[由包负责人在constant目录编写并形成文档分发],类内共享常量[自己维护如:private static final SUCCESS = "success"; ]
  • 如果常量值是仅仅在一个范围内变化的枚举类,而且还带有名称之外的延伸属性,必须使用枚举类
    例:public Enum {MONDAY(1),TUESDAY(2),WEDNESDAY(3)...} 其中数字是延伸信息。

格式规约

  • 大括号,如果大括号内为空可直接写成{},如接口方法,不需换行;如果是非空代码块则:
    1)左大括号前加空格不换行,其后换行
    2)右括号前换行,右大括号后如有else等代码则不换行,其它换行。
  • 左括号和后一个字符,右括号和前一个字符之间不出现空格,但是if/for/while/switch/do等java保留字符与作业括号之间都必须加空格。
    3)任何运算符左右必须加空格 如:if (flag == true) {}
    4)缩进统一采用4个空格,禁止使用tab字符串,使用tab缩进必须调整一个tab为4个空格,IDEA中设置tab为4个空格是请勿勾选Use tab character。
  • 单行字符数不超过120个,用idea开发不超过分隔黄线,超出则需要换行(最后不要写这么长),换行原则:
    1)第二行相对第一行缩进4个字符,第三行起不再继续缩进,最多不能超过10行。
    2)运算符和下文一起换行。
    3)方法调用的点符号和下文一起换行。
    4)多个参数超长,逗号后进行换行。(自己写的方法不允许超过三个参数)
    5)在括号前不要换行。如下错误的示例:
method(args1, args2, ...
    , argsX)
  • 方法参数在定义和传入是,多个参数逗号后必须加空格如:
method("a", "b", "c");
  • IDE中的文件编码均使用UTF-8,文件中的换行符使用Unix格式"\r",不使用windows格式"\r\n"
  • 方法中执行的与剧组,变量定义语句组、不同的业务逻辑之间或者不同语义之间可插入一个空行。

OOP规约

  • 不要使用一个类的对象访问类的静态变量和静态方法,这样会增加编译器的解析成本,务必使用类名直接访问。
  • 所有的覆写方法,必须加@Override注解
  • 相同的参数类型,相同的业务含义,且业务上一定要求需要可变参数,才可以使用可变参数,可变参数不能使用Object类型,可变参数必须防止到参数列表的最后。能少用可变参数,尽量不要使用可变参数方法。
  • 不使用过时的类和方法,如Date类中的getDay()一般文档里面都会有提示替代的方法。
  • Object的equals方法容易产生空指针异常,因此可以反过来使用常量或者确定值来和对象对比。如:
"test".equals(object)
  • 所有的相同类型的包装类对象之间的值比较,全部使用equals方法比较。
  • 基本数据类型和包装数据类型使用标准
    1)所有的POJO类属性必须使用包装数据类型(需要在以后的工作中严格执行)。
    2)RPC方法的返回值和参数必须使用包装数据类型。
    3)局部变量建议使用基本数据类型。
    NPE(Null Point Exception)问题,POJO类属性没有初始值是提醒使用者在需要使用的时候,必须显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。而POJO如果包含基本数据类型则会有默认值不会暴露问题。
  • 定义DO/VO等POJO类时,不要设置任何默认值。因为设置默认值可能引发别的影响,如DO对象中一般会有创建时间这个字段,可能会想到给其设置一个new Date()的默认值,但是当更新的时候忘记给其设置原有的值,进而导致更新后创建时间和更新时间变成一样的了。
  • 序列化类新增属性时,请不要修改serialVersionUID字段。在完全不兼容升级的情况下,为了避免反序列化混乱,请修改serialVersionUID值。
    说明:serialVersionUID不一致会出现序列号运行时异常。
  • 构造方法里禁止添加任何业务逻辑。如需初始化逻辑,请添加init方法。
  • POJO类必须添加toString方法。使用IDE可快速生成。
  • POJO类建议使用lombok的注解减少代码。
  • 使用索引访问String的split方法得到的数组时,请先检查数组大小,避免出现IndexOutOfBoundsException(数组越界)风险。
  • 一个类有多个构造方法,或者多个同名方法,请按照顺序放置到一起便于阅读。
  • 类内的方法定义顺序依次为:共有方法,受保护的方法>私有方法>getter>setter方法。一般来说共有方法是调用和维护者最关心的方法因此最先展示比较妥当。
  • 循环字符串连接请使用StringBuilder的append方法(StringBuilder是线程非安全的如需考虑线程安全可以使用StringBuffer)。
  • 使用final可提高程序运行效率,如下情况可使用final
    1)不需要重新复制的变量,类属性、局部变量。
    2)不允许修改引用的对象参数。
    3)类中确定不允许重新的方法。
  • 对象clone方法默认为浅拷贝慎用,如要实现深拷贝需要重新clone方法建议使用BeanCopier。
  • 类成员和方法访问控制从严,规则如下:
    1)如不允许外部直接通过new来创建对象,则其所有的构造方法均为private。
    2)工具类不允许有public和default的构造函数(建议只有一个private的空构造函数)。
    3)类的非static成员变量且于子类共享,必须为protected。
    4)类成员变量且仅限于本类使用,必须为private。
    5)static成员变量,考虑是否为final。
    6)类成员方法只供类内部使用,必须为private。
    7)类成员方法只对子类公开,则限制为protected。
    过于宽泛的访问范围,不利于模块解耦。private方法删除仅仅只要考虑类内部的调用即可,如果是public则删除的时候要特别小心。

集合处理

  • hashCode和equals的处理(对象作为集合中的key或者值需要注意的点)规则如下:
    1)重新了equals方法必须重新hashCode(如Map中键来定位对象先对比hashCode然后再用equals比较)
    2)Set存储的是不重复对象依据的也是hashCode和equals
    3)如果使用自定义对象作为Map的key则必须重新euqals和hashCode。
    如String冲向了hashCode和equals因此可以用来做Map的key使用。
  • ArrayList的subList返回的结果是ArrayList的内部类SubList并不是ArrayList需要注意。
  • 使用subList方法,需要注意原始集合元素个数的变化会导致子列表的遍历、增加删除产生ConcurrentModificationException异常。
  • 使用集合转数组方法,必须使用集合的toArray(T[] array),传入的是类型一样的数组,大小为list.size()。直接使用toArray无参函数返回值为Object[]需要强转会可能会出现类型转化错误。
  • 使用工具类Arrays.asList()把数组转化成集合时,不能使用其修改集合相关的方法add,remove,clear会抛出UnSupportedOperationException异常。且修改数组中的值,转化后的集合内的值也会发生变化。
    asList返回对象是一个Arrays内部类,没有实现集合的修改方法。且后台的数据任然是数组。
  • 不要在循环中进行元素的remove和add操作。remove元素请使用Iterator方式,如有并发需要对其加锁。
    正例:
  Iterator it = list.iterator();
  while(it.hasNext()) {
      String temp = it.next();
      if (condition) {
         it.remove();
      }
  }
  • 集合初始化,尽量指定集合的初始值大小。
    如:ArrayList尽量使用ArrayList(int initialCapacity) 初始化。
  • Map遍历使用Map.foreach方法(JDK8版本)
  • Map类集合k/v存储null值情况表格:


    研发二部JAVA后台开发规约(参考阿里JAVA代码规范)_第2张图片
    map.png
  • 注意利用集合的有序性(sort)和稳定性(order),以及无序性(unsort)和稳定性(unorder)可能引发的错误。
    有序性指遍历的结果按照某种比较规则依次排序,稳定性则是指每次遍历的元素次序是一样的。 ArrayList是order/unsort,HashMap是unsort/unorder的而TreeSet是sort/order的。实际开发中如页面中遍历集合想要每次进入页面的结果一样的顺序那从后台传回来的集合就不能是unorder的因为如果是unorder每次遍历的元素次序是不一样的。
  • 可以利用Set元素唯一的特性对集合快速去重(只需遍历一次即可)。

未完待续

你可能感兴趣的:(研发二部JAVA后台开发规约(参考阿里JAVA代码规范))