目录
- 1 编程规约
- 1.1 方法参数类型必须一致,不要出现自动装箱拆箱操作
- 1.1.1 反例
- 1.1.2 正例
- 1.2 SimpleDateFormat是线程不安全的
- 1.2.1 反例
- 1.2.2 正例
- 1.3 使用equals方法应该注意空指针
- 1.3.1 反例
- 1.3.2 正例
- 2 异常日志
- 2.1 事务场景中如果异常被被捕获要注意回滚
- 2.1.1 反例
- 2.1.2 正例
- 2.2 不要在 finally 块中使用 return
- 2.2.1 反例
- 2.2.2 正例
- 2.3 应用中不可直接使用日志系统(Log4j、Logback)中的 API
- 2.4 所有日志文件至少保存15天
- 3 单元测试
- 3.1 好的单元测试必须遵守 AIR 原则
- 3.2 单元测试应该是全自动执行的,并且非交互式的
- 3.3 单元测试是可以重复执行的,不能受到外界环境的影响
- 4 安全规约
- 4.1 用户敏感数据禁止直接展示,必须对展示数据进行脱敏
- 4.2 用户请求传入的任何参数必须做有效性验证
- 5 MySQL 数据库
- 5.1 表明命名规范
- 5.2 表必备的几个字段
- 6 工程结构
- 6.1 线上应用不要依赖 SNAPSHOT 版本
- 6.2 注意POM坐标冲突
- 7 阿里规范插件安装
- 7.1 IDEA安装该插件步骤
- 7.2 使用
- 7.3 检查等级
- 7.4 查看结果
1 编程规约
我们介绍了让代码规范的方案,下面我们就来说一下阿里的代码规范文档
业界公认的代码规范手册(国内)当属阿里巴巴旗下出版的《Java 开发手册》,经过几个版本的迭代,最新手册为《Java 开发手册》,更新时间为2022年2月3号。
手册以 Java 开发者为中心视角,划分为编程规约、异常日志、 单元测试、 安全规约、 MySQL 数据库、 工程结构、 设计规约七个维度,再根据内容特征,细分成若干二级子目录。根据约束力强弱及故障敏感性,规约依次分为强制、推荐、参考三大类。对于规约条目的延伸信息中,“说明” 对规约做了适当扩展和解释;“正例” 提倡什么样的编码和实现方式;“反例”说明需要提防的雷区, 以及真实的错误案例。
1.1 方法参数类型必须一致,不要出现自动装箱拆箱操作
1.1.1 反例
这种操作很容易产生难以排查的NPE异常
/**
* 反例
* 容易出现空指针异常,如果参数为null就会拆箱失败空指针错误
* 排查的时候很难排查,因为直接看代码看起来不会出现空指针的。
* @param value
* @return
*/
public static int handel(Integer value) {
return value;
}
1.1.2 正例
入参以及出参,和参数传递类型是一致的
public static Integer handel(Integer value) {
return value;
}
1.2 SimpleDateFormat是线程不安全的
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static, 必须加锁,或者使用 DateUtils 工具类
1.2.1 反例
使用这种方式在多线程的情况下会报错,或者出现数据不一致的情况
private static final SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
1.2.2 正例
使用ThreadLocal的方式保证线程安全,或者使用
DateTimeFormatter
代替SimpleDateFormat
private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
1.3 使用equals方法应该注意空指针
1.3.1 反例
如果object为null就会抛出NPE异常
object.equals("test");
1.3.2 正例
应该使用常量或者确定的值来进行判断equals
"test".equals(object);
或者使用
Objects.equals
Objects.equals(object1,object2)
2 异常日志
2.1 事务场景中如果异常被被捕获要注意回滚
2.1.1 反例
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
try{
//some code
//db operation
}catche(Exception e){
}
}
}
2.1.2 正例
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
try{
//some code
//db operation
}catche(Exception e){
//捕获处理后需要再将异常抛出
throw e;
}
}
}
2.2 不要在 finally 块中使用 return
try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。
2.2.1 反例
这种情况下结果永远返回的都是true,fanally中不要做返回操作
public static boolean getValue(String text) {
try {
return text.equals("123");
} finally {
return true;
}
}
2.2.2 正例
public static boolean getValue(String text) {
return "123".equals(text);
}
2.3 应用中不可直接使用日志系统(Log4j、Logback)中的 API
在Java生态体系中,围绕着日志,有很多成熟的解决方案,关于日志输出,主要有两类工具。
一类是日志框架,主要用来进行日志的输出的,比如输出到哪个文件,日志格式如何等,另外一类是日志门面,主要一套通用的API,用来屏蔽各个日志框架之间的差异的。
所以,对于Java工程师来说,关于日志工具的使用,最佳实践就是在应用中使用如Log4j + SLF4J 这样的组合来进行日志输出。
这样做的最大好处,就是业务层的开发不需要关心底层日志框架的实现及细节,在编码的时候也不需要考虑日后更换框架所带来的成本,这也是门面模式所带来的好处。
请不要在你的Java代码中出现任何Log4j等日志框架的API的使用,而是应该直接使用SLF4J这种日志门面。
2.4 所有日志文件至少保存15天
所有日志文件至少保存15天,因为有些异常具备以“周”为频次发生的特点,网络运行状态、安全相关信息、系统监测、管理后台操作、用户敏感操作需要留存相关的网络日志**不少于6个月**。
3 单元测试
3.1 好的单元测试必须遵守 AIR 原则
单元测试在线上运行时,感觉像空气(AIR)一样并不存在,但在测试质量的保障上,却是非常关键的,好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。
3.2 单元测试应该是全自动执行的,并且非交互式的
- 测试用例通常是被定期执行的,执行过程必须完全自动化才有意义。
- 输出结果需要人工检查的测试不是一个好的单元测试。
- 单元测试中不准使用
System.out
来进行人肉验证,必须使用assert
来验证。
3.3 单元测试是可以重复执行的,不能受到外界环境的影响
单元测试通常会被放到持续集成中,每次有代码check in时单元测试都会被执行,如果单测对外部环境(网络、服务、中间件等)有依赖,容易导致持续集成机制的不可用,为了不受外界环境影响,要求设计代码时就把SUT的依赖改成注入,在测试时用spring 这样的DI框架注入一个本地(内存)实现或者Mock实现。
4 安全规约
4.1 用户敏感数据禁止直接展示,必须对展示数据进行脱敏
中国大陆个人手机号码显示为:`137****0969`,隐藏中间 4 位,防止隐私泄露,以及用户的身份证号码,银行卡号码,用户姓名等都需要进行脱敏处理
4.2 用户请求传入的任何参数必须做有效性验证
说明:忽略参数校验可能导致:
- page size 过大导致内存溢出
- 恶意 order by 导致数据库慢查询
- 缓存击穿
- 服务器端请求伪造(SSRF)
- 任意重定向
- SQL 注入,Shell 注入,反序列化注入
- 正则输入源串拒绝服务 ReDoS
5 MySQL 数据库
5.1 表明命名规范
表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。
数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑,MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写,因此,数据库名、表名、 字段名,都不允许出现任何大写字母,避免节外生枝
- 一般以t_xxx来作为表名
- 一般以v_xxx来作为视图名称
5.2 表必备的几个字段
- 创建人:标记记录的初始创建人
- 创建时间:标记初始的创建人
- 修改人:标记修改人
- 修改时间:标记最后修改日期
- 版本号:用于统一化的乐观锁
6 工程结构
6.1 线上应用不要依赖 SNAPSHOT 版本
正式发布的类库必须先去中央仓库进行查证,使 RELEASE 版本号有延续性,且版本号不允许覆盖升级。
6.2 注意POM坐标冲突
禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的Version。
在本地调试时会使用各子项目指定的版本号,但是合并成一个 war,只能有一个版本号出现在最后的lib 目录中,曾经出现过线下调试是正确的,发布到线上却出故障的先例。
7 阿里规范插件安装
这里面只列出来了几个比较重要的,很有很多的规约没有写出来,为了让大家码代码的效率更高,可以安装阿里的代码规范插件来约束自己凌乱的代码
阿里规范插件GitHub地址:https://github.com/alibaba/p3c
7.1 IDEA安装该插件步骤
打开IDEA,File-> Setteings->Plugins,在搜索栏搜索Alibaba,然后安装,安装完后点击Restart IntelliJ IDEA重启idea
7.2 使用
安装好了,使用方法是:打开IDEA,点击tools—>安装的阿里编码规约,可以选择中英文切换,项目右键选择编码规约扫描就可以进行查看自己编码哪些地方不够好
7.3 检查等级
检查结果一共分三个等级
等级 | 验证程度 | 备注 |
---|---|---|
Blocker | 严重 | 有明显问题,影响功能以及性能 |
Critical | 危险 | 一般问题,遵循标准 |
Major | 主要 | 一般为建议 |
7.4 查看结果
可以通过结果查看一般存在的问题