阿里巴巴的java開發規範,值得去熟悉和了解

一、編程規約

 

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服務器所支持的最大句柄數調高數倍(與服務器的內存數量相關)。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章