一、编程规约

1.【强制】抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类的名称开始,以Test结尾
2.【强制】POJO类中布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误。
  反例:定义为基本数据类型Boolean isDeleted;的属性,它的方法也是isDeleted(),RPC框架在反向解析的时候,“以为”对应的属性名称是deleted,导致属性获取不到,进而抛出异常。
3.【强制】杜绝完全不规范的缩写,避免望文不知义。 
  反例:AbstractClass“缩写”命名成AbsClass;condition“缩写”命名成 condi,此类随意缩写严重降低了代码的可阅读性。
4.【推荐】如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。 
  正例:public class OrderFactory; public class LoginProxy; public class ResourceObserver;
5.【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的Javadoc注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。 
  正例:接口方法签名:void f(); 接口基础常量表示:String COMPANY = "alibaba"; 
  反例:接口方法定义:public abstract void f(); 
  说明:JDK8中接口允许有默认实现,那么这个default方法,是对所有实现类都有价值的默认实现。
6.接口和实现类的命名有两套规则: 
  1)【强制】对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别。 
    正例:CacheServiceImpl实现CacheService接口。 
  2)【推荐】 如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able的形式)。 
    正例:AbstractTranslator实现 Translatable。
7.【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中。 反例:String key = "Id#taobao_" + tradeId; cache.put(key, value);
  反例:String key = "Id#taobao_" + tradeId; cache.put(key, value);
8.【推荐】不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。 说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
  正例:缓存相关常量放在类CacheConsts下;系统配置相关常量放在类ConfigConsts下。
9.【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果是非空代码块则: 
  1) 左大括号前不换行。 
  2) 左大括号后换行。 
  3) 右大括号前换行。 
  4) 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
10.【强制】if/for/while/switch/do等保留字与括号之间都必须加空格。
11.【强制】任何二目、三目运算符的左右两边都需要加一个空格。 
  说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
12.【强制】注释的双斜线与注释内容之间有且仅有一个空格。 
  正例:// 注释内容,注意在//和注释内容之间有一个空格。
13.【强制】单行字符数限不超过 120 个,超出需要换行时 个,超出需要换行时 遵循如下原则:
  1) 第二行相对一缩进 4个空格,从第三行开始不再继续缩进参考示例。 
  2) 运算符与下文一起换行。 
  3) 方法调用的点符号与下文一起换行。 
  4) 方法调用时,多个参数,需要换行时,在逗号后进行。 
  5) 在括号前不要换行,见反例。 
  正例:
    StringBuffer sb = new StringBuffer();
    // 超过120个字符的情况下,换行缩进4个空格,点号和方法名称一起换行
    sb.append("zi").append("xin")...
        .append("huang")...
        .append("huang")...
        .append("huang");
  反例:
    StringBuffer sb = new StringBuffer();
    // 超过120个字符的情况下,不要在括号前换行
    sb.append("zi").append("xin")...append
        ("huang");
    // 参数很多的方法调用可能超过120个字符,不要在逗号前换行
    method(args1, args2, args3, ...
        , argsX);
14.【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。 
  正例:下例中实参的"a",后边必须要有一个空格。
  method("a", "b", "c");
15.【强制】所有的覆写方法,必须加@Override注解。
16.【强制】Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。 
  正例:"test".equals(object); 
  反例:object.equals("test");
  说明:推荐使用java.util.Objects#equals(JDK7引入的工具类)
17.【强制】所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。 
  说明:对于Integer var = ? 在-128至127范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
18.关于基本数据类型与包装数据类型的使用标准如下: 
  1) 【强制】所有的POJO类属性必须使用包装数据类型。 
  2) 【强制】RPC方法的返回值和参数必须使用包装数据类型。 
  3) 【推荐】所有的局部变量使用基本数据类型。 
  说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE问题,或者入库检查,都由使用者来保证。 
  正例:数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有NPE风险。 
  反例:比如显示成交总额涨跌情况,即正负x%,x为基本数据类型,调用的RPC服务,调用不成功时,返回的是默认值,页面显示为0%,这是不合理的,应该显示成中划线。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败,异常退出。
19.【强制】定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。 
  反例:POJO类的gmtCreate默认值为new Date();但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
20.【强制】序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。 
  说明:注意serialVersionUID不一致会抛出序列化运行时异常。
21.【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
22.【强制】POJO类必须写toString方法。
  说明:在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。
23.【推荐】使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛IndexOutOfBoundsException的风险。
  说明:
    String str = "a,b,c,,";
    String[] ary = str.split(",");
    // 预期大于3,结果是3
    System.out.println(ary.length);
24.【推荐】 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法。
25.【推荐】循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。 说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
  反例:
    String str = "start";
    for (int i = 0; i < 100; i++) {
    str = str + "hello";
    }
26.【推荐】类成员与方法访问控制从严: 
  1) 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。 
  2) 工具类不允许有public或default构造方法。 
  3) 类非static成员变量并且与子类共享,必须是protected。
  4) 类非static成员变量并且仅在本类使用,必须是private。 
  5) 类static成员变量如果仅在本类使用,必须是private。
  6) 若是static成员变量,必须考虑是否为final。 
  7) 类成员方法只供类内部调用,必须是private。 
  8) 类成员方法只对继承类公开,那么限制为protected。
  说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。
  思考:如果是一个private的方法,想删除就删除,可是一个public的service方法,或者一个public的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,无限制的到处跑,那么你会担心的。
27.【强制】关于hashCode和equals的处理,遵循如下规则: 
  1) 只要重写equals,就必须重写hashCode。 
  2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。 
  3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。 
  说明:String重写了hashCode和equals方法,所以我们可以非常愉快地使用String对象作为key来使用。
28.【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常,即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList. 
  说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
29.【强制】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
  正例:
    Iterator<String> iterator = list.iterator();
      while (iterator.hasNext()) {
        String item = iterator.next();
      if (删除元素的条件) {
        iterator.remove();
      }
    } 
  反例:
    List<String> list = new ArrayList<String>();
      list.add("1");
      list.add("2");
    for (String item : list) {
      if ("1".equals(item)) {
        list.remove(item);
      }
    }
  说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗?
30.【推荐】集合初始化时,指定集合初始值大小。 说明:HashMap使用HashMap(int initialCapacity) 初始化, 
  正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意 负载因子(即loader factor)默认为 0.75,如果 暂时无法 确定 初始值大小,请设置为 16(即默认值)。 
  反例: HashMap需要 放置 1024个元素, 由于 没有设置容量 初始大小,随着元素不断增加容 量 7次被迫扩大, resize需要重建 hash表,严重影响性能。 
31.【参考】利用Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用List的contains方法进行遍历、对比、去重操作。
32.【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
33.【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 
  说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
34.【强制】线程池不允许使用 【强制】线程池不允许使用 Executors ExecutorsExecutors Executors ExecutorsExecutors去创建,而是通过 去创建,而是通过 去创建,而是通过 ThreadPoolExecutor ThreadPoolExecutorThreadPoolExecutor ThreadPoolExecutor ThreadPoolExecutor ThreadPoolExecutor ThreadPoolExecutorThreadPoolExecutor ThreadPoolExecutor ThreadPoolExecutorThreadPoolExecutor的方式,这样 的方式,这样 的处理方式让写同学更加明确线程池运行规则,避资源耗尽风险。 说明: Executors ExecutorsExecutors Executors ExecutorsExecutors返回的线程池对象 返回的线程池对象 的弊端 如下 : 
  1)FixedThreadPool 和 SingleThreadPool: 
    允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 
  2)CachedThreadPool 和 ScheduledThreadPool: 
    允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
35.【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。 
  正例:注意线程安全,使用DateUtils。亦推荐如下处理:
    private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
      @Override
      protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
      }
    };
  说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
36.【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 
  说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法。
37.【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。 
  说明:线程一需要对表A、B、C依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是A、B、C,否则可能出现死锁。
38.【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。 
  说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。
39.【推荐】避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
  说明:Random实例包括java.util.Random 的实例或者 Math.random()的方式。 
  正例:在JDK7之后,可以直接使用API ThreadLocalRandom,而在 JDK7之前,需要编码保证每个线程持有一个实例。
40.【参考】HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中可以使用其它数据结构或加锁来规避此风险。
41.【强制】在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
42.【推荐】表达异常的分支时,少用if-else方式,这种方式可以改写成:
    if (condition) {
      ...
      return obj;
    }
    // 接着写else的业务逻辑代码;
    说明:如果非得使用if()...else if()...else...方式表达逻辑,【强制】避免后续代码维护困难,请勿超过3层。
    正例:超过3层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句示例如下:
    public void today() {
      if (isBusy()) {
        System.out.println(“change time.”);
        return;
      }
      if (isFree()) {
        System.out.println(“go to travel.”);
        return;
      }
      System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”);
      return;
    }
43.【推荐】除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。 
  说明:很多if语句内的逻辑相当复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么,如果阅读者分析逻辑表达式错误呢? 
  正例:
    // 伪代码如下
    final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
    if (existed) {
    ...
    }
  反例: 
    if ((file.open(fileName, "w") != null) && (...) || (...)) {
    ...
    }
44.【强制】类、类属性、类方法的注释必须使用Javadoc规范,使用/**内容*/格式,不得使用// xxx方式。
45.【强制】所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 
  说明:对子类的实现要求,或者调用注意事项,请一并说明。
46.【强制】所有的类都必须添加创建者和创建日期。
47.【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
48.【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。 
  1) 待办事宜(TODO):( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个Javadoc的标签,目前的Javadoc还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个Javadoc标签)。 
  2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间]) 在注释中用FIXME标记某代码是错误的,而且不能工作,需要及时纠正的情况。
49.【强制】注意 Math.random() 这个方法返回是double类型,注意取值的范围 0≤x<1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后取整,直接使用Random对象的nextInt或者nextLong方法。
50.【推荐】不要在视图模板中加入任何复杂的逻辑。

二、异常日志


1.【强制】Java 类库中定义的一类RuntimeException可以通过预先检查进行规避,而不应该通过catch 来处理,比如:IndexOutOfBoundsException,NullPointerException等等。
  说明:无法通过预检查的异常除外,如在解析一个外部传来的字符串形式数字时,通过catch NumberFormatException来实现。 
  正例:if (obj != null) {...} 反例:try { obj.method() } catch (NullPointerException e) {...}
2.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
3.【强制】有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务。
4.【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。 
  说明:如果JDK7及以上,可以使用try-with-resources方式。
5.【强制】应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    private static final Logger logger = LoggerFactory.getLogger(Abc.class);
6.【强制】日志文件推荐至少保存15天,因为有些异常具备以“周”为频次发生的特点。
7.【强制】避免重复打印日志,浪费磁盘空间,务必在log4j.xml中设置additivity=false
  正例:<logger name="com.taobao.dubbo.config" additivity="false">
8.【参考】可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别,error级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,请不要在此场景打出error级别。
9.【强制】单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测试中不准使用System.out来进行人肉验证,必须使用assert来验证。


三、单元测试


1.【强制】保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序。
2.【强制】核心业务、核心应用、核心模块的增量代码确保单元测试通过。
3.【推荐】单元测试的基本目标:语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都要达到100% 说明:在工程规约的应用分层中提到的DAO层,Manager层,可重用度高的Service,都应该进行单元测试。
4.【推荐】对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的,或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。 
  反例:删除某一行数据的单元测试,在数据库中,先直接手动增加一行作为删除目标,但是这一行新增数据并不符合业务插入规则,导致测试结果异常。
5.【推荐】和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者对单元测试产生的数据有明确的前后缀标识。 
  正例:在RDC内部单元测试中,使用RDC_UNIT_TEST_的前缀标识数据。


四、安全规约


1.【强制】隶属于用户个人的页面或者功能必须进行权限控制校验。
2.【强制】用户敏感数据禁止直接展示,必须对展示数据进行脱敏。
3.【强制】用户输入的SQL参数严格使用参数绑定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL访问数据库。
4.【强制】用户请求传入的任何参数必须做有效性验证。 
  说明:忽略参数校验可能导致:
     page size过大导致内存溢出
     恶意order by导致数据库慢查询
     任意重定向
     SQL注入
     反序列化注入
     正则输入源串拒绝服务ReDoS 
  说明: Java JavaJava代码用 代码用 正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的 结果。
5.【强制】禁止向HTML页面输出未经安全过滤或未正确转义的用户数据。
6.【强制】表单、AJAX提交必须执行CSRF安全过滤。 
  说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在CSRF漏洞的应用/网站,攻击者可以事先构造好URL,只要受害者用户一访问,后台便在用户不知情情况下对数据库中用户参数进行相应修改。
7.【强制】表单、AJAX提交必须执行CSRF安全过滤。 
  说明:CSRF(Cross-site request forgery)跨站请求伪造是一类常见编程漏洞。对于存在CSRF漏洞的应用/网站,攻击者可以事先构造好URL,只要受害者用户一访问,后台便在用户不知情情况下对数据库中用户参数进行相应修改。
8.【推荐】发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。


五、MySQL数据库


1.【强制】表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint( 1表示是,0表示否)。 
  说明:任何字段如果为非负数,必须是unsigned。 正例: 表达逻辑删除的字段名 is_deleted,1表示删除, 0表示未删除。 
2.【强制】小数类型为decimal,禁止使用float和double。
3.【强制】varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
4.【强制】表必备三字段:id, gmt_create, gmt_modified。 
  说明: 其中 id必为 主键,类型必为 主键,类型unsigned bigint、单表时自增步长为 、单表时自增步长为 1。gmt_create, gmt_modified的类型均为 的类型均为 date_time类型,前者现在时表示主动创建后过去分词被 类型,前者现在时表示主动创建后过去分词被 类型,前者现在时表示主动创建后过去分词被 动
5.【推荐】表的命名最好是加上“业务名称_表的作用”。
6.【推荐】单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
7.【强制】禁用保留字,如 desc、range、match、delayed 等,禁止在代码里对 SQL 关键字进行单独处理。
8.【强制】不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
  说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。
9.【强制】 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
10.【强制】不得使用外键与级联,一切外键概念必须在应用层解决。 
  说明:以学生和成绩的关系为例,学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
11.【强制】数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误才能执行更新语句。
12.【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
13.【强制】POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行字段与属性之间的映射。 
  说明:参见定义POJO类以及数据库字段定义规定,在<resultMap>中增加映射,是必须的。在MyBatis Generator生成的代码中,需要进行对应的修改。
14.【强制】sql.xml配置参数使用:#{},#param# 不要使用${} 此种方式容易出现SQL注入。
15.【强制】iBATIS自带的queryForList(String statementName,int start,int size)不推荐使用。  
  说明:其实现方式是在数据库取到statementName对应的SQL语句的所有记录,再通过subList取start,size的子集合。
  正例: Map<String, Object> map = new HashMap<String, Object>();
        map.put("start", start);
        map.put("size", size);
16.【强制】不允许直接拿HashMap与Hashtable作为查询结果集的输出。
17.【强制】更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。
18.【推荐】不要写一个大而全的数据更新接口。传入为POJO类,不管是不是自己的目标更新字段,都进行update table set c1=value1,c2=value2,c3=value3; 这是不对的。执行SQL时,不要更新无改动的字段,一是易出错;二是效率低;三是增加binlog存储。
19.【参考】@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
20.【参考】<isEqual>中的compareValue是与属性值对比的常量,一般是数字,表示相等时带上此条件;<isNotEmpty>表示不为空且不为null时执行;<isNotNull>表示不为null值时执行。


六、工程结构


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:详细规定参考下方。
2.【强制】二方库版本号命名方式:主版本号.次版本号.修订号 
  1) 主版本号:产品方向改变,或者大规模API不兼容,或者架构不兼容升级。 
  2) 次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的API不兼容修改。 
  3) 修订号:保持完全兼容性,修复BUG、新增次要功能特性等。 
  说明: 注意起始版本号 必须 为: 1.0.0,而不是 0.0.1 正式发布的类库必须先去中央仓库进行查证,使版本号有延续性,正式版本号不允许覆盖升级。如当前版本:1.3.3,那么下一个合理的版本号:1.3.4 或 1.4.0 或 2.0.0
3.【强制】依赖于一个二方库群时,必须定义一个统一的版本变量,避免版本号不一致。 
  说明:依赖springframework-core,-context,-beans,它们都是同一个版本,可以定义一个变量来保存版本:${spring.version},定义依赖的时候,引用该版本。
4.【推荐】所有pom文件中的依赖声明放在<dependencies>语句块中,所有版本仲裁放在<dependencyManagement>语句块中。
5.【推荐】高并发服务器建议调小TCP协议的time_wait超时时间。
6.【推荐】调大服务器所支持的最大文件句柄数(File Descriptor,简写为fd)。 
  说明:主流操作系统的设计是将TCP/UDP连接采用与文件一样的方式去管理,即一个连接对应于一个fd。主流的linux服务器默认所支持最大fd数量为1024,当并发连接数很大时很容易因为fd不足而出现“open too many files”错误,导致新的连接无法建立。 建议将linux服务器所支持的最大句柄数调高数倍(与服务器的内存数量相关)。