Java开发手册规则精选

一 编程规约

(一)命名风格

1.【强制】所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。

正例:ali / alibaba / taobao / kaikeba / aliyun / youku / hangzhou 等国际通用的名称,可视同英文。

反例:DaZhePromotion【打折】/ getPingfenByName()【评分】 / String fw【福娃】

【推荐】为了达到代码自解释的目标,任何自定义编程元素在命名时,使用完整的单词组合来表达。

正例:在JDK,对某个对象引用的volatile字段进行原子更新的类AtomicReferenceFieldUpdater。

反例:常见的方法内变量为int a; 的定义方式。

2.【强制】类名使用UpperCamelCase风格,以下情形例外:DO / PO / DTO / BO / VO / UID等。 正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion

反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion

【强制】方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格。

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

【推荐】在常量与变量命名时,表示类型的名词放在词尾,以提升辨识度。

即:类名用大驼峰,方法变量用小驼峰

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

4.【强制】类型与中括号紧挨相连来定义数组。 正例:定义整形数组int[] arrayDemo。 反例:在main参数中,使用String args[] 来定义。

即:变量名放在最后

(二)常量定义

1.【强制】long或Long赋值时,数值后使用大写L,不能是小写l,小写容易跟数字混淆,造成误解。

2.【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

即:常量可以按照功能的不同分成多个类

(三)代码格式

1.【强制】任何二目、三目运算符的左右两边都需要加一个空格。
说明:包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
2.【强制】注释的双斜线与注释内容之间有且仅有一个空格。

java 复制代码
// 这是注释

3.【强制】注释的双斜线与注释内容之间有且仅有一个空格。

java 复制代码
double first =3.2d;
int second = (int)first+2

4.【强制】方法参数在定义和传入时,多个参数逗号后面必须加空格。

5.【推荐】不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。
说明:任何情形,没有必要插入多个空行进行隔开。
(四)OOP规约
1.
【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成
本,直接用类名来访问即可。
即:引用时不能是新命名的方法,应该为他本来的原方法
2.【强制】。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
3.【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
(不懂)
4.【强制】定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。
5.【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果
完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。
说明:注意 serialVersionUID 不一致会抛出序列化运行时异常
(看不懂)
6.【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
(又看不懂)
7.【推荐】使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容
的检查,否则会有抛 IndexOutOfBoundsException 的风险。

java 复制代码
String str = "a,b,c,,";
String[] ary = str.split(",");
// 预期大于 3,结果是 3
System.out.println(ary.length);

8.【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter
方法。
9.【推荐】类成员与方法访问控制从严:如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。 工具类不允许有 public 或 default 构造方法。 类非 static 成员变量并且与子类共享,必须是 protected。 类非 static 成员变量并且仅在本类使用,必须是 private。 类 static 成员变量如果仅在本类使用,必须是 private。 若是 static 成员变量,考虑是否为 final。 类成员方法只供类内部调用,必须是 private。 类成员方法只对继承类公开,那么限制为 protected
(五)日期时间
1.【强制】在日期格式中分清楚大写的 M 和小写的 m,大写的 H 和小写的 h 分别指代的意义。
说明:日期格式中的这两对字母表意如下: 表示月份是大写的 M; m表示分钟则是小写的 m; 24 小时制的是大写的 H; 12 小时制的则是小写的 h。
2.【强制】获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime()。
3.【强制】不允许在程序任何地方中使用:java.sql.Date。 java.sql.Time。java.sql.Timestamp。
(六)集合处理
1.【强制】在使用 java.util.stream.Collectors 类的 toMap()方法转为 Map 集合时,一定要使
用含有参数类型为 BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同 key
值时会抛出 IllegalStateException 异常。
2.【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
3.【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator
方式,如果并发操作,需要对 Iterator 对象加锁。
例:

java 复制代码
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
 String item = iterator.next();
 if (删除元素的条件) {
 iterator.remove();
 }
}

(七)并发处理

1.【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者"过度切换"的问题。
2.【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
3.【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能
锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。
线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、 B、C,否则可能出现死锁。
4.【强制】在使用阻塞等待获取锁的方式中,必须在 try 代码块之外,并且在加锁方法与 try 代
码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally 中无法解锁。
说明一:如果在 lock 方法与 try 代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功
获取锁。
说明二:如果 lock 方法在 try 代码块之内,可能由于其它方法抛出异常,导致在 finally 代码块中,unlock
对未加锁的对象解锁,它会调用 AQS 的 tryRelease 方法(取决于具体实现类),抛出
IllegalMonitorStateException 异常。
说明三:在 Lock 对象的 lock 方法实现中可能抛出 unchecked 异常,产生的后果与说明二相同。
5.【强制】在使用尝试机制来获取锁的方式中,进入业务代码块之前,必须先判断当前线程是否
持有锁。锁的释放规则与锁的阻塞等待方式相同。
说明:Lock 对象的 unlock 方法在执行时,它会调用 AQS 的 tryRelease 方法(取决于具体实现类),如果 当前线程不持有锁,则抛出 IllegalMonitorStateException 异常。

java 复制代码
Lock lock = new XxxLock();
// ...
boolean isLocked = lock.tryLock();
if (isLocked) {
 try {
 doSomething();
 doOthers();
 } finally {
 lock.unlock();
 }
}

6.【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加
锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。
7.【推荐】使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方
法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至
await 方法,直到超时才返回结果。
说明:注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
8.【推荐】避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed导致的性能下降。
说明:Random 实例包括 java.util.Random 的实例或者 Math.random()的方式。
9.【推荐】通过双重检查锁(double-checked locking)(在并发场景下)存在延迟初始化的优化
问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration),推荐解决方案中较
为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型,比如将 helper 的属
性声明修改为`private volatile Helper helper = null;`。

java 复制代码
public class LazyInitDemo {
 private volatile Helper helper = null;
 public Helper getHelper() {
 if (helper == null) {
 synchronized (this) {
 if (helper == null) { helper = new Helper(); }
 }
 }
 return helper;
 }
 // other methods and fields... 
}

10.【参考】volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但
是如果多写,同样无法解决线程安全问题。
说明:如果是 count++操作,使用如下类实现:AtomicInteger count = new AtomicInteger();
count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观 锁的重试次数)。
11.【参考】HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在
开发过程中注意规避此风险。
12.【参考】ThreadLocal 对象使用 static 修饰,ThreadLocal 无法解决共享对象的更新问题。
说明:这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量, 也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可 以操控这个变量。
(八)控制语句
1.【强制】在高并发场景中,避免使用"等于"判断作为中断或退出的条件。
说明:如果并发控制没有处理好,容易产生等值判断被"击穿"的情况,使用大于或小于的区间判断条件来代替。
反例:判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,这样的话,活动无法终止。
2.【推荐】当某个方法的代码总行数超过 10 行时,return / throw 等中断逻辑的右大括号后均
需要加一个空行。
说明:这样做逻辑清晰,有利于代码阅读时重点关注。
(九)注释规约
1.【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用
// xxx 方式。
2.【强制】所有的类都必须添加创建者和创建日期。

java 复制代码
/**
* @author yangguanbao
* @date 2016/10/31
*/

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

(十)前后端规约

1.【强制】前后端交互的 API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响
应体。
说明: 1) 协议:生产环境必须使用 HTTPS。 2) 路径:每一个 API 需对应一个路径,表示 API 具体的请求地址: a) 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。 b) URL 路径不能使用大写,单词如果需要分隔,统一使用下划线。 c) 路径禁止携带表示请求内容类型的后缀,比如".json",".xml",通过 accept 头表达即可。 3) 请求方法:对具体操作的定义,常见的请求方法如下: a) GET:从服务器取出资源。 b) POST:在服务器新建一个资源。 c) PUT:在服务器更新资源。 d) DELETE:从服务器删除资源。 4) 请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type。 5) 响应体:响应体 body 可放置多种数据类型,由 Content-Type 头来确定。
2.【强制】在前后端交互的 JSON 格式数据中,所有的 key 必须为小写字母开始的
lowerCamelCase 风格,符合英文表达习惯,且表意完整。
正例:errorCode / errorMessage / assetStatus / menuList / orderList / configFlag
反例:ERRORCODE / ERROR_CODE / error_message / error-message / errormessage /
ErrorMessage / msg
3.【强制】HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。
说明:不同浏览器对于 URL 的最大长度限制略有不同,并且对超出最大长度的处理逻辑也有差异,2048 字节是取所有浏览器的最小值。
反例:某业务将退货的商品 id 列表放在 URL 中作为参数传递,当一次退货商品数量过多时,URL 参数超长, 传递到后端的参数被截断,导致部分商品未能正确退货。
4.【强制】HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出
错。
说明:nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调 大服务器端的限制。
5.【强制】服务器内部重定向必须使用 forward;外部重定向地址必须使用 URL 统一代理模块
生成,否则会因线上采用 HTTPS 协议而导致浏览器提示"不安全",并且还会带来 URL 维护
不一致的问题。
(十一)其他
1.【强制】后台输送给页面的变量必须加!{var}------中间的感叹号。 说明:如果 var 等于 null 或者不存在,那么{var}会直接显示在页面上。
2.【强制】注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够
取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后
取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
3.【推荐】及时清理不再使用的代码段或配置信息。
说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///)
来说明注释掉代码的理由。如:

java 复制代码
public static void hello() {
 /// 业务方通知活动暂停
 // Business business = new Business();
 // business.active();
 System.out.println("it's finished");
}

二异常日志

(一)错误码

1.【强制】全部正常,但不得不填充错误码时返回五个零:00000

2.【强制】错误码使用者避免随意定义新的错误码。
说明:尽可能在原有错误码附表中找到语义相同或者相近的错误码在代码中使用即可。
3.【推荐】在获取第三方服务错误码时,向上抛出允许本系统转义,由 C 转为 B,并且在错误信
息上带上原有的第三方错误码。
4.【参考】错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。
说明:在无法更加具体确定的错误场景中,可以直接使用一级宏观错误码,分别是:A0001(用户端错误)、B0001(系统执行出错)、C0001(调用第三方服务出错)。
正例:调用第三方服务出错是一级,中间件错误是二级,消息服务出错是三级。
5.【参考】错误码的后三位编号与 HTTP 状态码没有任何关系。
6.【参考】错误码即人性,感性认知+口口相传,使用纯数字来进行错误码编排不利于感性记忆
和分类。
说明:数字是一个整体,每位数字的地位和含义是相同的。
反例:一个五位数字 12345,第 1 位是错误等级,第 2 位是错误来源,345 是编号,人的大脑不会主动地拆开并分辨每位数字的不同含义。
(二)异常处理
1.【强制】异常捕获后不要用来做流程控制,条件控制。
说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
2.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请
将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的
内容。
3.【强制】在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable
类来进行拦截。
说明:通过反射机制来调用方法,如果找不到方法,抛出 NoSuchMethodException。什么情况会抛出NoSuchMethodError 呢?二方包在类冲突时,仲裁机制可能导致引入非预期的版本使类的方法签名不匹配,或者在字节码修改框架(比如:ASM)动态创建或修改类时,修改了相应的方法签名。这些情况,即使代码编译期是正确的,但在代码运行期时,会抛出 NoSuchMethodError。
(不明白)
4.【推荐】防止 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 问题。
5.【参考】对于公司外的 http/api 开放接口必须使用 errorCode;而应用内部推荐异常抛出;
跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess()方法、errorCode、
errorMessage;而应用内部直接抛出异常即可。
说明:关于 RPC 方法返回方式使用 Result 方式的理由:
1)使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。
2)如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。
(三)日志规约
1.【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架
(SLF4J、JCL--Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
说明:日志框架(SLF4J、JCL--Jakarta Commons Logging)的使用方式(推荐使用 SLF4J)
使用 SLF4J,使用 JCL

java 复制代码
//使用SLF4J
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
//使用JCL
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
private static final Log log = LogFactory.getLog(Test.class);

2.【强制】所有日志文件至少保存 15 天,因为有些异常具备以"周"为频次发生的特点。对于
当天日志,以"应用名.log"来保存,保存在/home/admin/应用名/logs/目录下,过往日志
格式为: {logname}.log.{保存日期},日期格式:yyyy-MM-dd
正例:以 aap 应用为例,日志保存在/home/admin/aapserver/logs/aap.log,历史日志名称为
aap.log.2016-08-01
3.【强制】生产环境禁止直接使用 System.out 或 System.err 输出日志或使用
e.printStackTrace()打印异常堆栈。
说明:标准日志输出与标准错误输出文件每次 Jboss 重启时才滚动,如果大量输出送往这两个文件,容易 造成文件大小超过操作系统大小限制。
4.【强制】对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。
说明:虽然在 debug(参数)的方法体内第一行代码 isDisabled(Level.DEBUG_INT)为真时(Slf4j 的常见实现 Log4j 和 Logback),就直接 return,但是参数可能会进行字符串拼接运算。此外,如果 debug(getName()) 这种参数内有 getName()方法调用,无谓浪费方法调用的开销。
正例:

java 复制代码
// 如果判断为真,那么可以输出 trace 和 debug 级别的日志
if (logger.isDebugEnabled()) {
 logger.debug("Current ID is: {} and name is: {}", id, getName());
}

5.【推荐】可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适
从。如非必要,请不要在此场景打出 error 级别,避免频繁报警。
说明:注意日志输出的级别,error 级别只记录系统逻辑出错、异常或者重要的错误信息。
三单元测试
1.【强制】单元测试应该是全自动执行的,并且非交互式的。测试用例通常是被定期执行的,执
行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元
测试中不准使用 System.out 来进行人肉验证,必须使用 assert 来验证。
2.【强制】核心业务、核心应用、核心模块的增量代码确保单元测试通过。
说明:新增代码及时补充单元测试,如果新增代码影响了原有单元测试,请及时修正。
3.【强制】好的单元测试必须遵守 AIR 原则。
说明:单元测试在线上运行时,感觉像空气(AIR)一样感觉不到,但在测试质量的保障上,却是非常关键 的。好的单元测试宏观上来说,具有自动化、独立性、可重复执行的特点。 A:Automatic(自动化) I:Independent(独立性) R:Repeatable(可重复)
4.【推荐】编写单元测试代码遵守 BCDE 原则,以保证被测试模块的交付质量。
B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
C:Correct,正确的输入,并得到预期的结果。
D:Design,与设计文档相结合,来编写单元测试。
E:Error,强制错误信息输入(如:非法数据、异常流程、业务允许外等),并得到预期的结果。
5.【推荐】和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者对
单元测试产生的数据有明确的前后缀标识。
正例:在阿里巴巴企业智能事业部的内部单元测试中,使用 *ENTERPRISE_INTELLIGENCE UNIT_TEST*的前缀来标识单元测试相关代码。
四安全规约

  1. 【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
    说明:中国大陆个人手机号码显示:139****1219,隐藏中间 4 位,防止隐私泄露。
    2.【强制】用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。
    反例:某系统签名大量被恶意修改,即是因为对于危险字符 # --没有进行转义,导致数据库更新时,where 后边的信息被注释掉,对全库进行更新。
    3.【强制】表单、AJAX 提交必须执行 CSRF 安全验证。
    说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在 CSRF 漏洞的应用网站,攻击者可以事先构造好 URL,只要受害者用户一访问,后台便在用户不知情的情况下对数据库中用 户参数进行相应修改。
    五Mysql数据表
    (一)建表规约
    1.【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。
    说明:pk_ 即 primary key;uk_ 即 unique key;idx_ 即 index 的简称。
    2.【强制】小数类型为 decimal,禁止使用 float 和 double。
    说明:在存储的时候,float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的 结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数并分开存储。
    3.【强制】表必备三字段:id, create_time, update_time。
    说明:其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。create_time, update_time
    的类型均为 datetime 类型,前者现在时表示主动式创建,后者过去分词表示被动式更新。
    4.【推荐】表的命名最好是遵循"业务名称_表的作用"。
    正例:alipay_task / force_project / trade_config
    5.【推荐】字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
    1) 不是频繁修改的字段。
    2) 不是唯一索引的字段。
    3) 不是 varchar 超长字段,更不能是 text 字段。
    正例:各业务线经常冗余存储商品名称,避免查询时需要调用 IC 服务获取。
    (二)索引规约
    1.【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
    说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外, 即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。
    2.【强制】超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,
    保证被关联的字段需要有索引。
    说明:即使双表 join 也要注意表索引、SQL 性能。
    (三)sql语句
    (四)ORM映射
  2. 【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
    说明:1)增加查询分析器解析成本。2)增减字段容易与 resultMap 配置不一致。3)无用字段增加网络消耗,尤其是 text 类型的字段。
  3. 【强制】POJO 类的布尔属性不能加 is,而数据库字段必须加 is_,要求在 resultMap 中进行
    字段与属性之间的映射。
    说明:参见定义 POJO 类以及数据库字段定义规定,在 sql.xml 增加映射,是必须的。
  4. 【强制】不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要
    定义<resultMap>;反过来,每一个表也必然有一个<resultMap>与之对应。
    说明:配置映射关系,使字段与 DO 类解耦,方便维护。
  5. 【强制】sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
  6. 【强制】iBATIS 自带的 queryForList(String statementName,int start,int size)不推荐使用。
    说明:其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList 取 start,size 的子集合。
    正例:
java 复制代码
Map<String, Object> map = new HashMap<>(16);
map.put("start", start);
map.put("size", size);
  1. 【强制】不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。
    反例:某同学为避免写一个<resultMap>xxx</resultMap>,直接使用 HashTable 来接收数据库返回结果,结果出现日常是把 bigint 转成 Long 值,而线上由于数据库版本不一样,解析成 BigInteger,导致线
    上问题。
  2. 【强制】更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。
  3. 【推荐】不要写一个大而全的数据更新接口。传入为 POJO 类,不管是不是自己的目标更新字
    段,都进行 update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行 SQL 时,
    不要更新无改动的字段,一是易出错;二是效率低;三是增加 binlog 存储。
  4. 【参考】@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需
    要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
    10.【参考】<isEqual>中的 compareValue 是与属性值对比的常量,一般是数字,表示相等时
    带上此条件;<isNotEmpty>表示不为空且不为 null 时执行;<isNotNull>表示不为 null 值
    时执行。
    六工程结构
    (一)应用分层
    1.【参考】分层领域模型规约:
    • DO(Data Object):此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
    • DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
    • BO(Business Object):业务对象,可以由 Service 层输出的封装业务逻辑的对象。
    • Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
    • VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
    (二)二方依赖库
    1.【强制】定义 GAV 遵从以下规则:
    1) GroupID 格式:com.{公司/BU }.业务线 [.子业务线],最多 4 级。
    说明:{公司/BU} 例如:alibaba/taobao/tmall/aliexpress 等 BU 一级;子业务线可选。
    正例:com.taobao.jstorm 或 com.alibaba.dubbo.register
    2) ArtifactID 格式:产品线名-模块名。语义不重复不遗漏,先到中央仓库去查证一下。
    正例:dubbo-client / fastjson-api / jstorm-tool
    3) Version:详细规定参考下方。
  5. 【强制】二方库版本号命名方式:主版本号.次版本号.修订号
    1)主版本号:产品方向改变,或者大规模 API 不兼容,或者架构不兼容升级。
    2) 次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的 API 不兼容修改。
    3) 修订号:保持完全兼容性,修复 BUG、新增次要功能特性等。
    说明:注意起始版本号必须为:1.0.0,而不是 0.0.1。
  6. 【强制】线上应用不要依赖 SNAPSHOT 版本(安全包除外);正式发布的类库必须先去中央仓
    库进行查证,使 RELEASE 版本号有延续性,且版本号不允许覆盖升级。
    说明:不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。
  7. 【强制】二方库的新增或升级,保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变,
    必须明确评估和验证。
    说明:在升级时,进行 dependency:resolve 前后信息比对,如果仲裁结果完全不一致,那么通过
    dependency:tree 命令,找出差异点,进行<exclude>排除 jar 包。
  8. 【强制】二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚
    举类型或者包含枚举类型的 POJO 对象。
  9. 【强制】依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。
    说明:依赖 springframework-core,-context,-beans,它们都是同一个版本,可以定义一个变量来保存版 本:${spring.version},定义依赖的时候,引用该版本。
  10. 【强制】禁止在子项目的 pom 依赖中出现相同的 GroupId,相同的 ArtifactId,但是不同的
    Version。
    说明:在本地调试时会使用各子项目指定的版本号,但是合并成一个 war,只能有一个版本号出现在最后的 lib 目录中。曾经出现过线下调试是正确的,发布到线上却出故障的先例。
  11. 【推荐】底层基础技术框架、核心数据管理平台、或近硬件端系统谨慎引入第三方实现。
  12. 【推荐】所有 pom 文件中的依赖声明放在<dependencies>语句块中,所有版本仲裁放在
    <dependencyManagement>语句块中。
    说明:<dependencyManagement>里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖,version 和 scope 都读取自父 pom。而<dependencies>所有声明在主 pom 的<dependencies>里的依 赖都会自动引入,并默认被所有的子项目继承。
    10.【推荐】二方库不要有配置项,最低限度不要再增加配置项。
    11.【推荐】不要使用不稳定的工具包或者 Utils 类。
    说明:不稳定指的是提供方无法做到向下兼容,在编译阶段正常,但在运行时产生异常,因此,尽量使用 业界稳定的二方工具包。
    (三)服务器
  13. 【推荐】高并发服务器建议调小 TCP 协议的 time_wait 超时时间。
    说明:操作系统默认 240 秒后,才会关闭处于 time_wait 状态的连接,在高并发访问下,服务器端会因为 处于 time_wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。
    正例:在 linux 服务器上请通过变更/etc/sysctl.conf 文件去修改该缺省值(秒):
    net.ipv4.tcp_fin_timeout = 30
  14. 【推荐】调大服务器所支持的最大文件句柄数(File Descriptor,简写为 fd)。
    说明:主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd。 主流的linux服务器默认所支持最大fd数量为1024,当并发连接数很大时很容易因为fd不足而出现"open too many files"错误,导致新的连接无法建立。建议将 linux 服务器所支持的最大句柄数调高数倍(与服 务器的内存数量相关)。
  15. 【推荐】给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出 dump 信息。
    说明:OOM 的发生是有概率的,甚至相隔数月才出现一例,出错时的堆内信息对解决问题非常有帮助。
  16. 【推荐】在线上生产环境,JVM 的 Xms 和 Xmx 设置一样大小的内存容量,避免在 GC 后调整
    堆大小带来的压力。
  17. 【参考】服务器内部重定向必须使用 forward;外部重定向地址必须使用 URL Broker 生成,否
    则因线上采用 HTTPS 协议而导致浏览器提示"不安全"。此外,还会带来 URL 维护不一致的
    问题。
    七设计规约

注:本篇文章仅部分易错开发规则,如需查看全部,请自行搜索Java开发文档

相关推荐
小宇的天下2 小时前
Virtuoso 中的tech file 详细说明
java·服务器·前端
AI视觉网奇2 小时前
ue5.5 动画 选取 一段使用
笔记·学习·ue5
WX-bisheyuange2 小时前
基于SpringBoot的诊疗预约平台
java·spring boot·后端·毕业设计
世人万千丶2 小时前
鸿蒙跨端框架Flutter学习day 1、变量与基本类型-智能家居监控模型
学习·flutter·ui·智能家居·harmonyos·鸿蒙·鸿蒙系统
阳光九叶草LXGZXJ2 小时前
达梦数据库-学习-41-表大小快速估算
linux·运维·数据库·sql·学习
SimonKing2 小时前
基于Netty的WebSocket客户端
java·后端·程序员
世人万千丶2 小时前
鸿蒙跨端框架Flutter学习day 1、变量与基本类型-咖啡店点餐逻辑
学习·flutter·ui·交互·鸿蒙·鸿蒙系统
DYS_房东的猫2 小时前
《 C++ 零基础入门教程》第8章:多线程与并发编程 —— 让程序“同时做多件事”
开发语言·c++·算法
ekkcole2 小时前
java实现对excel模版填充保存到本地后合并单元格并通过网络下载
java·开发语言·excel