# Java后端开发规范
> 拟稿:[黎润豪](/lirh)(个人博客)
>
>
版本号 1.0.0
>
## 目录
- [Java后端开发规范](#java%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83)
- [前言](#%E5%89%8D%E8%A8%80)
- [编程约束](#%E7%BC%96%E7%A8%8B%E7%BA%A6%E6%9D%9F)
- [命名风格](#%E5%91%BD%E5%90%8D%E9%A3%8E%E6%A0%BC)
- [类命名](#%E7%B1%BB%E5%91%BD%E5%90%8D)
- [方法命名](#%E6%96%B9%E6%B3%95%E5%91%BD%E5%90%8D)
- [变量命名](#%E5%8F%98%E9%87%8F%E5%91%BD%E5%90%8D)
- [泛型](#%E6%B3%9B%E5%9E%8B)
- [代码结构](#%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84)
- [OOP规约](#oop%E8%A7%84%E7%BA%A6)
- [控制语句](#%E6%8E%A7%E5%88%B6%E8%AF%AD%E5%8F%A5)
- [注释规范](#%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83)
- [系统分层规范](#%E7%B3%BB%E7%BB%9F%E5%88%86%E5%B1%82%E8%A7%84%E8%8C%83)
- [controller层](#controller%E5%B1%82)
- [service层](#service%E5%B1%82)
- [Dao/Mapper层](#daomapper%E5%B1%82)
- [接口管理规范](#%E6%8E%A5%E5%8F%A3%E7%AE%A1%E7%90%86%E8%A7%84%E8%8C%83)
- [异常处理规范](#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E8%A7%84%E8%8C%83)
- [异常处理](#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86)
- [异常处理原则](#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%8E%9F%E5%88%99)
- [异常处理的编码规范](#%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E7%9A%84%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83)
- [日志规约](#%E6%97%A5%E5%BF%97%E8%A7%84%E7%BA%A6)
- [工程结构](#%E5%B7%A5%E7%A8%8B%E7%BB%93%E6%9E%84)
## 前言
本文为公司Java后端开发的代码规范,规范分如下两级:
-  :开发者必须遵循的开发规范要求
-  :开发者选择性遵循的开发规范要求
原则:约定至上;Keep Simple。(周志尧指定)
约定优于配置。
## 编程约束
-  禁止在个人编写的代码,注释或文档中添加任何非业务或说明相关的外部链接。
### 命名风格
-  代码中的命名不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
-  杜绝完全不规范的缩写,避免望文不知义。
>  :AbstractClass缩写成AbsClass;condition缩写成condi;Function缩写成Fu。
-  严禁使用非全世界都知道的拼音命名。
>  :alibaba/youku/hangzhou
>  :suanFen【算分】,getGTHTypeCode【获取个体户类型编码】
#### 类命名
-  采用大驼峰(UpperCamelCase)命名法,禁止使用数字命名。
>  :KeyPair,GlobalConfig
>  :areaSlice,AreaSlice1,AreaSlice2
-  采用【名词】,【形容词+名词】的格式进行命名
>  :SMSSender,GlobalConfig
>  :SendSMS
-  抽象类必须以Abstract或Base开头(工具类例外:如StringUtils);异常类名明必须以Exception结尾;测试类必须以Test结尾。
-  接口的参数类必须要以VO或Dto结尾命名;接口与接口之间的传输参数传输类必须以Dto结尾命名。
#### 方法命名
-  采用小驼峰(LowerCamelCase)命名法,禁止使用拼音命名
-  方法参数不多于5个,多于5个后改用DTO进行传输。
#### 变量命名
-  常量与枚举均使用大写+下划线组合的方式命名
>  LIMIT_SIZE,ENTITY_TYPE
-  采用小驼峰(LowerCamelCase)命名法,禁止使用拼音命名
-  局部非循环与下标变量,不可使用单字母命名(特殊数学含义例外,如斜截式的斜率a,常数b)
### 泛型
-  涉及泛型的类,严禁不写前泛。
 :
```java
List resultList = new ArrayLisy<>();
```
 :
```java
List resultList = new ArrayLisy();
Map resultList = new HashMap();
```
### 代码结构
-  采用 4 个空格缩进,禁止使用 tab 字符。IDEA 设置 tab 为 4 个空格时,请勿勾选 `Use tab character`;(设置界面 `editor -> code style -> java`)
-  单行字符数限制不超过120个,超出需要换行; (设置界面 `File -> Settings -> Code Sytle` 中勾选`Wrap on typing`选项,`Right Margin`为设置的字符数限制)
-  `{}`代码块嵌套不应超过4层
-  单个方法的总行数不超过 100 行。
> 说明:除注释之外的方法签名、左右大括号、方法内代码、空行、回车及任何不可见字符的总行数不超过 100行。代码逻辑分清红花和绿叶,个性和共性,绿叶逻辑单独出来成为额外方法,使主干代码更加清晰;共性逻辑抽取成为共性方法,便于复用和维护。
### OOP规约
-  构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,放在 init 方法中。
-  当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。
-  类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter 方法。
> 说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法;而私有方法外部一般不需要特别关心,是一个黑盒实现;因为承载的信息价值较低,所有 Service 和 DAO 的 getter/setter 方法放在类体最后。
-  `setter`方法中,参数名称与类成员变量名称一致,`this.成员名 = 参数名`。在`getter/setter`方法中,不要增加业务逻辑,增加排查问题的难度。
-  循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
> 说明:下例中,反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
### 控制语句
-  除常用方法(如`getXxx/isXxx`)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
> 说明:很多 if 语句内的逻辑表达式相当复杂,与、或、取反混合运算,甚至各种方法纵深调用,理解成 本非常高。如果赋值一个非常好理解的布尔变量名字,则是件令人赏心悦目的事情。
-  不要在其它表达式(尤其是条件表达式)中,插入赋值语句。
> 说明:赋值点类似于人体的穴位,对于代码的理解至关重要,所以赋值语句需要清晰地单独成为一行。
-  循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变 量、获取数据库连接,进行不必要的`try - catch`操作(这个`try - catch`是否可以移至循环体外)。
### 注释规范
-  类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用 // xxx 方式。
> 说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
-  所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
-  所有的类都必须添加创建者和创建日期。
-  方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释 使用/**/注释,注意与代码对齐。
-  所有的枚举类型字段必须要有注释,说明每个数据项的用途。
-  代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。
-  谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
-  对于注释的要求:第一、能够准确反映设计思想和代码逻辑 ; 第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路 ; 注释也是给继任者看的,使其能够快速接替自己的工作。
-  好的命名、代码结构是自解释的,注释力求精简准确、表达到位。
## 系统分层规范
### controller层
-  仅接收参数和拼凑返回数据,不写具体的业务逻辑, 所有业务逻辑必须写到service层里。
### service层
-  数据查询命名规则
a)获取单个对象的方法用`get`作前缀。
b)获取多个对象的方法用`list`作前缀。
c)获取统计值的方法用`count`作前缀。
d)插入的方法用`save`作前缀。
e)删除的方法用`remove`作前缀。
f)修改的方法用`modify/update`作前缀。
### Dao/Mapper层
-  数据查询命名规则
a)获取单个对象的方法用`select`作前缀。
b)获取多个对象的方法用`select`作前缀,`List`作后续,如:`selectXxxList`。
c)获取统计值的方法用`count`作前缀。
d)插入的方法用`insert`作前缀。
e)删除的方法用`delete`作前缀。
f)修改的方法用`update`作前缀。
## 接口管理规范
-  `controller`返回类型必须为`RestResult`。
-  `controller`返回的`RestResult`必须写泛型,若泛型无法确定,请写`>`。
-  一般编码中只使用get(接口只做查询,不修改数据库)和post(修改数据库)。
-  一般编码中只使用get(接口只做查询,不修改数据库)和post(修改数据库)。
-  避免出现复杂的输入参数,一般情况下,不允许出现例如json字符串这样的参数,这种参数可读性极差。应该定义对应的bean。
-  `controller`接口的方法参数不应超过5个。如果超过,则应定义相应的Bean进行传输。
## 异常处理规范
### 异常处理
异常类型主要有业务异常和应用系统异常两类:
1. 业务异常,由业务编码人员在ResultCode预先定义,根据业务逻辑进行设定,视作正常的功能返回(HTTP Status Code 200 OK);
2. 业务系统异常,影响业务正常运转的异常,均视为系统功能性异常。

### 异常处理原则
异常处理,总的原则是所有出现的异常必须要有处理;
1. 所有业务异常,前端必须要有相应的响应;
2. 所有的系统异常,后端必须要有日志记录与统一结果返回。
### 异常处理的编码规范

1. 后端编码不要捕获任何异常
不要在业务代码中编写捕获异常的代码, 即 dao、service、controller 层的所有的异常都全部抛出到上层;
业务代码中只处理正确的功能, 不对异常做任何判断,后端切忌通过异常类型、或者异常出现与否来决定业务逻辑;
2. 后端统一处理异常
业务代码中不捕获异常, 系统所有异常都会抛出到 Controller 层,系统基础框架会统一拦截,由日志框架记录异常栈的信息,并统一返回标准的结果信息RestResult;
3. 前端统一处理
前端组件对后端返回结果集进行统一处理,对不同类型的异常进行相应业务逻辑的处理;
-  方法若有显式的RuntimeException异常抛出,必须使用`throws`关键字将其标识到方法上
-  异常的打印均用日志对象的error等级输出
-  IDEA发现的空指针问题必须要处理掉
-  捕获异常和跑异常必须完全匹配,或者捕获异常是抛异常的父类
-  若异常被捕获,如果数据需要回滚,一定要注意进行手动回滚
## 日志规约
-  日志对象在项目引用了lombok的情况下,请用@SLF4J注解声明
-  所有日志文件至少保存15天
-  日志信息拼接使用`{}`占位符,以提高可读性和拼接性能
-  生产环境进制直接使用`System.out`,`System.err`或`e.printStackTrace()`充当日志输出
-  避免重复打印日志,浪费磁盘空间,务必在日志配置文件中的子logger配置additivity=false
```xml
```
## 工程结构
-  包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
- :用工具类包名为`com.alibaba.ai.util`、类名为`MessageUtils`
-  目录结构com.chinaweal.{项目简称}.{通用配置名/数据源缩写}.{业务板块名}.{controller/service/entity/mapper}