最近在整理公司编码规范方面的内容,2017
年阿里巴巴
发布了编码规范插件,强烈建议大家安装使用,好的编码习惯是通往成功的阶梯。
专题 | 专题名称 | 专题描述 |
---|---|---|
001 | Spring Boot 核心技术 | 讲解SpringBoot一些企业级层面的核心组件 |
002 | Spring Cloud 核心技术 | 对Spring Cloud核心技术全面讲解 |
003 | QueryDSL 核心技术 | 全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA |
004 | SpringDataJPA 核心技术 | 全面讲解SpringDataJPA核心技术 |
类、类属性使用Javadoc
规范,类上描述该类的主要作用,注释尽可能详细,推荐把使用该类地方使用@see
注解进行标注,类属性详细描述该属性的保存内容。
类注释示例
:
/**
* 统一资源Aop切面定义
* 根据自定义注解配置自动设置配置的资源类型到指定的字段
* @author:于起宇 <br/>
* ===============================
* Created with IDEA.
* Date:2017/12/15
* Time:14:05
* ================================
*/
类属性注释示例
:
/**
* 资源处理业务逻辑
*/
Autowired
ResourcePushService resourcePushService;
接口方法、实现类方法、抽象方法等,详细描述该方法主要作用,尽可能的描述出方法的主要流程步骤,方法定义的每一个参数都需要有详细的注释描述,建议添加方法返回值描述。
方法注释示例
:
/**
* 资源设置切面方法
* 拦截配置了@ResourceMethod注解的class method
* cglib仅支持class 方法切面,接口切面不支持
* @param proceedingJoinPoint 切面方法实例
* @param resourceMethod 方法注解实例
* @return 原方法返回对象
*/
@Around(value = "@annotation(resourceMethod)")
public Object resourcePutAround(ProceedingJoinPoint proceedingJoinPoint, ResourceMethod resourceMethod)
throws Throwable
{
//...
}
如果是业务逻辑方法注释建议添加步骤,如下所示:
/**
* 创建帖子
* - 转换参数实体为
* - 保存帖子基本信息
* @param param 创建帖子请求参数实体
* @return 帖子编号
* @throws LogicException
*/
public String create(CreateTopicParam param) throws LogicException {
//...
}
在上面代码中-
分隔符代表主要执行步骤,每一个步骤以-
分隔符开始,如果方法内存在逻辑分支处理,请看下面行注释
。
行级注释一般都是方法内使用到,分为单行注释、多行注释,单行注释采用//
设置,多行注释采用/* */
设置,如下所示:
单行注释
:
// 执行方法,获取返回值
多行注释
:
/*
* 执行方法,获取返回值
* 获取返回值进行后续逻辑处理
*/
我们在实际开发过程中数据库对应的实体是不允许直接拿出来添加一些附加字段的,也就是禁止添加非该数据表对应实体内的字段,这种情况我们需要定义DTO/Param
。
DTO
是数据返回实体定义,如果我们在查询数据库时需要关联其他表的数据并且返回给前端,那么我们可以创建XxxDTO
,注意:DTO
全部大写,只需要继承查询逻辑的主表实体就可以完成附加字段的添加,要为每一个附加字段添加javadoc
详细注释,如下所示:
/**
* 帖子列表数据转换实体
* @author:于起宇 <br/>
* ===============================
* Created with IDEA.
* Date:2018/1/8
* Time:10:52
* ================================
*/
@Data
public class TopicListDTO
extends CommunicationTopicInfoEntity {
/**
* 用户昵称
*/
private String userNickName;
/**
* 所属机构名称
*/
private String orgName;
//...
}
对于接口、后台来说接受请求时一般都是带着一些参数,目前我们系统是前台完全分离,所以后台其实变相的也是接口,在上面DTO
也有说到数据实体不允许添加附加参数,我们的参数也不可能都是数据实体内的字段,这时需要创建对应的参数实体XxxParam
,参数实体内的所有字段都需要添加javadoc
注释,如下所示:
/**
* 查询帖子列表
* - 用于查询自己、他人、关键字、首页帖子请求参数
*
* @author:于起宇 <br/>
* ===============================
* Created with IDEA.
* Date:2018/1/8
* Time:10:31
* ================================
*/
@Data
public class SelectTopicParam extends PagerParam {
/**
* 用户编号
*/
private String userId;
/**
* 查询关键字
*/
@Length(max = 30)
private String keyWord;
}
错误示例
:
_name / name_ / $name / name$ / __name / name__
错误示例
:
WenZhang[文章] / WenZhangInfo[文章信息] / getTopicLieBiao()[获取文章列表] / int 数量 = 0
UpperCamelCase
风格,DTO
、VO
除外错误示例
:
QRCode / UserInfoDto / XMLService
正确示例
:
QrCode / UserInfoDTO / XmlService
lowerCamelCase
风格,必须使用驼峰命名方式。错误示例
:
localvalue / GetUserInfo() / userid
正确示例
:
localValue / getUserInfo() / userId
错误示例
:
MAX_COUNT / NAME
正确示例
:
MAX_STOCK_COUNT / DEFAULT_ORG_NAME
Abstract
或者Base
开头,异常类命名使用Exception
结尾,测试类命名时以将要测试的类全名 + Test
。正确示例
:
AbstractCodeMessageService / BaseCodeMessageService / LogicException / UserControllerTest
正确示例
:
com.sanmi.framework.core / com.sanmi.framework.orm
错误示例
:
AbstractClass = > AbsClass
condition => condi
javadoc
注释。错误示例
:
public abstract void commit();
正确示例
:
void commin(); / String COMPANY_NAME = "sanmi";
Service
和DAO
类命名时实现类后缀要以Impl
结尾,Mapper
接口不存在实现类,无需处理。正确示例
:
UserInfoService / UserInfoServiceImpl
Enum
后缀,枚举成员名称需要全大写,单词间使用下划线隔开。正确示例
:
枚举名称 => UserStatusEnum
成员名称 => ENABLE / DISABLED / DELETE
Service/DAO/Mapper
层方法命名规约如下: get
作为前缀list
作为前缀count
作为前缀save / insert
作为前缀delete / remove
作为前缀update
作为前缀错误示例
:
if("enable".equals(user.getStatus())) {
//..
}
正确示例
:
Integer => int
Double => double
Long => long
...
enum
来代替定义正确示例
:
@Getter
public enum UserBzEnum
{
/**
* 普通用户
*/
APP_USER("0"),
/**
* 月嫂
*/
NANNY("1"),
/**
* 医生
*/
DOCTOR("2")
;
private String value;
UserBzEnum(String value) {
this.value = value;
}
}
{}
即可,不需要换行;如果非空代码,则需要: 错误示例
:if ( a == b )
正确示例
:
if (ValidateTools.isEmpty(uploadBackEntity) || ValidateTools.isEmpty(uploadBackEntity.getUploadUrl())) {
//...
}
Tab
控制符。//
与注释内容之间有且仅有一个空格。正确示例
:
// 定义用户名
String userName = user.getName();
+
).
符号一起换行正确示例
:
StringBuffer buffer = new StringBuffer();
// 超过120个字符的情况下,换行缩进4个空格,``.``和方法名一块换行
buffer.append("san").append("mi") ...
.append("ke")
.append("ji");
// 参数过多超120个字符时,在 , 后换行
method(args1, args2, args3, ...
args98, args99
)
正确示例
:method(args1, args2);
@Overrider
注解@Deprecated
注解,并清晰的说明替代接口或者替代服务。Object
的equals
方法容易抛出空指针异常,应使用常量或确定值的对象来调用equals
方法。错误示例
:
object.equals("test");
正确示例
:
"test".equals(object)
equals
方法。说明
:int
封装类Integer
在-128 ~ 127
范围内的赋值会在IntegerCache.cache
中产生,该区间的值可以直接使用==
进行比对,但是该区间外的值都会以引用类型在堆
内创建,对象之间是无法使用==
来进行比对的!
init
方法使用。toArray(T[] array)
,传入的是类型完全一样的数组,大小则是list.size()
。正确示例
:
List<String< list = new ArrayList();
list.add("jinan");
list.add("sanmi");
// 生成list长度的数组实例
String[] array = new String[list.size()];
// 执行集合转换数据
array = list.toArray(array);
Arrays.asList()
方法时,不能使用修改集合相关的方法,add / remove / clean
方法会抛出异常。foreach
循环内进行元素的remove / add
操作,remove
元素请使用Iterator
方式,如果并发操作,需要对Iterator
对象加锁。正确示例
:
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (condition) {
iterator.remove();
}
}
switch
内,每个case
要么使用break / return
来终止,要么注释说明程序将继续执行到具体的哪个case
为止;在一个switch
内必须包含default
代码块在所有case
之后,就算业务逻辑为空也要存在!if / else / for / while / do
语句中必须使用大括号,即便是只有一行代码,也需要添加大括号。错误示例
:
if (condition) statements;
正确示例
:
if (condition) {
statements;
}
if / else if
嵌套方式,可以修改成:if (condition) {
//...
return object;
}
// 继续处理 else / else if 业务逻辑代码
如果不得不使用超过三层的if / else if
逻辑判断,可以将代码改成卫语句
、策略设计模式
、状态设计模式
,下面是卫语句示例:
public void today() {
if (isBuy()) {
System.out.println("do something.");
return;
}
if (isFree()) {
System.out.println("do something.");
return;
}
System.out.println("coding..");
}
在我们系统设计时,一般都会使用throw new XxxException();
将return;
代替,所有逻辑异常判断都采用自定义业务逻辑异常进行处理。
RuntimeException
,不应该采用try {} catch(Exception e){}
来处理。错误示例
:
// 直接使用不确定对象
object.setXxx(value);
正确示例
:
// 判断非空后使用不确定对象
if (object != null) {
//...
}
try
代码块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意rollback
事务。finally
代码块必须对资源对象、刘对象进行关闭操作,即使有异常也要做try-catch
操作。finally
代码块中使用return
。正确示例
:
if (condition) {
throw new LogicException(ErrorCodeEnum.USER_NOT_FOUND);
}
API
,而依赖使用日志框架SLF4j
中的API
。使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
正确示例
:import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 获取日志对象
private static final Logger logger = LoggerFactory.getLogger(Xxx.class);
日志对象定义一般都会在项目框架搭建初期定义,直接使用即可,如果需要自定义,则按照上面的方式进行声明日志对象。
trace / debug / info
级别的日志输出,必须使用占位符的方式,如果不使用占位符而是直接拼接,可能会导致变量为null
导致系统异常,还一点日志等级不匹配时虽然不会打印,但是会执行字符串的拼接,浪费服务器系统资源。正确示例
:
logger.debug("执行查询用户:{},基本信息。",user.getId());
正确示例
:
public void today(String userId) {
logger.debug("查询用户:{},今日任务完成进度",userId);
}
正确示例
:
if (condition1) {
logger.debug("do something.");
} else if (condition2) {
logger.debug("do other thing.");
}
案发现场
、异常堆栈信息
.正确示例
:
logger.error(参数或对象.toString() + "_" + e.getMessage(), e);
强烈建议IDEA
开发工具安装使用阿里巴巴
国际编码规约插件,为良好的编码习惯打下基础。