阿里巴巴Java開發規範手冊

 

 

Java開發手冊java

 

 

版本號linux

制定團隊程序員

更新日期web

備  注正則表達式

 

1.0.0spring

阿里巴巴集團技術部sql

2016.12.7數據庫

首次向Java業界公開express

       1、編程規約

(一) 命名規約

1.   【強制】全部編程相關命名均不能如下劃線或美圓符號開始,也不能如下劃線或美圓符號結束。反例: _name / __name / $Object / name_ / name$ / Object$apache

2.   【強制】全部編程相關的命名嚴禁使用拼音與英文混合的方式,更不容許直接使用中文的方式。說明:正確的英文拼寫和語法可讓閱讀者易於理解,避免歧義。注意,即便純拼音命名方式也要避免採用。

反例: DaZhePromotion [打折] / getPingfenByName() [評分] / int 變量 = 3; 正例: ali / alibaba / taobao /cainiao / aliyun / youku / hangzhou 等國際通用的名稱,可視爲英文。

3.   【強制】類名使用UpperCamelCase風格,必須聽從駝峯形式,但如下情形例外:(領域模型的相關命名)DO / DTO / VO / DAO等。

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 反例:macroPolo / UserDo /XMLService / TCPUDPDeal / TAPromotion

4.   【強制】方法名、參數名、成員變量、局部變量都統一使用lowerCamelCase風格,必須聽從駝峯形式。

正例:localValue / getHttpMessage() / inputUserId

5.   【強制】常量命名所有大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。正例: MAX_STOCK_COUNT 反例: MAX_COUNT

6.    【強制】抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結尾;測試類命名以它要測試的類的名稱開始,以Test結尾。

7.   【強制】中括號是數組類型的一部分,數組定義以下:String[] args; 反例:請勿使用String args[]的方式來定義

8.   【強制】POJO類中的任何布爾類型的變量,都不要加is,不然部分框架解析會引發序列化錯誤。

反例:定義爲基本數據類型boolean isSuccess;的屬性,它的方法也是isSuccess(),RPC

框架在反向解析的時候,「覺得」對應的屬性名稱是success,致使屬性獲取不到,進而拋出異常。

9.   【強制】包名統一使用小寫,點分隔符之間有且僅有一個天然語義的英語單詞。包名統一使用單數形式,可是類名若是有複數含義,類名可使用複數形式。

正例: 應用工具類包名爲com.alibaba.mpp.util、類名爲MessageUtils(此規則參考spring 的框架結構)

10.【強制】杜絕徹底不規範的縮寫,避免望文不知義。

反例:<某業務代碼>AbstractClass「縮寫」命名成AbsClass;condition「縮寫」命名成 condi,此類隨意縮寫嚴重下降了代碼的可閱讀性。

11.【推薦】若是使用到了設計模式,建議在類名中體現出具體模式。

說明:將設計模式體如今名字中,有利於閱讀者快速理解架構設計思想。

正例:public class OrderFactory;   public class LoginProxy;

   public classResourceObserver;

12.【推薦】接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,並加上有效的javadoc註釋。儘可能不要在接口裏定義變量,若是必定要定義變量,確定是與接口方法相關,而且是整個應用的基礎常量。

正例:接口方法簽名:void f();

   接口基礎常量表示:String COMPANY = "alibaba";

反例:接口方法定義:public abstract void f();

說明:JDK8中接口容許有默認實現,那麼這個default方法,是對全部實現類都有價值的默認實現。

13.接口和實現類的命名有兩套規則:

1)   【強制】對於Service和DAO類,基於SOA的理念,暴露出來的服務必定是接口,內部的實現類用Impl的後綴與接口區別。

正例:CacheServiceImpl實現CacheService接口。

2)   【推薦】 若是是形容能力的接口名稱,取對應的形容詞作接口名(一般是–able的形式)。

正例:AbstractTranslator實現 Translatable。

14.【參考】枚舉類名建議帶上Enum後綴,枚舉成員名稱須要全大寫,單詞間用下劃線隔開。

說明:枚舉其實就是特殊的常量類,且構造方法被默認強制是私有。

正例:枚舉名字:DealStatusEnum;成員名稱:SUCCESS / UNKOWN_REASON。

15.【參考】各層命名規約: 

A)  Service/DAO層方法命名規約

1) 獲取單個對象的方法用get作前綴。

2) 獲取多個對象的方法用list作前綴。

3) 獲取統計值的方法用count作前綴。

4) 插入的方法用save(推薦)或insert作前綴。

5) 刪除的方法用remove(推薦)或delete作前綴。

6) 修改的方法用update作前綴。

B)  領域模型命名規約

1) 數據對象:xxxDO,xxx即爲數據表名。

2) 數據傳輸對象:xxxDTO,xxx爲業務領域相關的名稱。

3) 展現對象:xxxVO,xxx通常爲網頁名稱。

4)  POJO是DO/DTO/BO/VO的統稱,禁止命名成xxxPOJO。

(二) 常量定義

1.   【強制】不容許出現任何魔法值(即未經定義的常量)直接出如今代碼中。

反例: String key="Id#taobao_"+tradeId;    cache.put(key, value);

2.   【強制】long或者Long初始賦值時,必須使用大寫的L,不能是小寫的l,小寫容易跟數字1 混淆,形成誤解。

說明:Longa = 2l; 寫的是數字的21,仍是Long型的2?

3.   【推薦】不要使用一個常量類維護全部常量,應該按常量功能進行歸類,分開維護。如:緩存相關的常量放在類:CacheConsts下;系統配置相關的常量放在類:ConfigConsts下。

說明:大而全的常量類,非得ctrl+f才定位到修改的常量,不利於理解,也不利於維護。

4.   【推薦】常量的複用層次有五層:跨應用共享常量、應用內共享常量、子工程內共享常量、包內共享常量、類內共享常量。

1) 跨應用共享常量:放置在二方庫中,一般是client.jar中的const目錄下。

2) 應用內共享常量:放置在一方庫的modules中的const目錄下。

  反例:易懂變量也要統必定義成應用內共享常量,兩位攻城師在兩個類中分別定義了表示

「是」的變量:

    類A中:public static final String YES ="yes";     類B中:public static final String YES = "y";

   A.YES.equals(B.YES),預期是true,但實際返回爲false,致使產生線上問題。

3) 子工程內部共享常量:即在當前子工程的const目錄下。

4) 包內共享常量:即在當前包下單獨的const目錄下。

5)  類內共享常量:直接在類內部private static final定義。

5.   【推薦】若是變量值僅在一個範圍內變化用Enum類。若是還帶有名稱以外的延伸屬性,必須使用Enum類,下面正例中的數字就是延伸信息,表示星期幾。

正例:public Enum{ MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4),FRIDAY(5),

SATURDAY(6), SUNDAY(7);}

(三) 格式規約

1.   【強制】大括號的使用約定。若是是大括號內爲空,則簡潔地寫成{}便可,不須要換行;若是是非空代碼塊則:

1) 左大括號前不換行。

2) 左大括號後換行。

3) 右大括號前換行。

4)  右大括號後還有else等代碼則不換行;表示終止右大括號後必須換行。

2.   【強制】 左括號和後一個字符之間不出現空格;一樣,右括號和前一個字符之間也不出現空格。詳見第5條下方正例提示。

3.    【強制】if/for/while/switch/do等保留字與左右括號之間都必須加空格。

4.   【強制】任何運算符左右必須加一個空格。

說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號、三目運行符等。

5.   【強制】代碼塊縮進4個空格,若是使用tab縮進,請設置成1個tab爲4個空格。

正例:(涉及1-5點)


public static void main(String args[]){     

// 縮進4個空格     

String say ="hello";     

// 運算符的左右必須有一個空格     

int flag = 0;    

// 關鍵詞if與括號之間必須有一個空格,括號內f與左括號,1與右括號不須要空格     if(flag == 0) {         

System.out.println(say);     

}              

// 左大括號前加空格且不換行;左大括號後換行     

if (flag == 1){         

System.out.println("world");     

// 右大括號前換行,右大括號後有else,不用換行     

} else{           

System.out.println("ok");     

// 右大括號作爲結束,必須換行      

6.【強制】單行字符數限制不超過120個,超出須要換行,換行時,遵循以下原則:  1) 換行時相對上一行縮進4個空格。

2) 運算符與下文一塊兒換行。

3) 方法調用的點符號與下文一塊兒換行。

4) 在多個參數超長,逗號後進行換行。

5)  在括號前不要換行,見反例。正例:

StringBuffer sb = new StringBuffer(); 

//超過120個字符的狀況下,換行縮進4個空格,而且方法前的點符號一塊兒換行  sb.append("zi").append("xin")…    

.append("huang"); 

反例:

StringBuffer sb = new StringBuffer(); 

//超過120個字符的狀況下,不要在括號前換行 

sb.append("zi").append("xin")…append     

("huang");  

 

//參數不少的方法調用也超過120個字符,逗號後纔是換行處 method(args1,args2, args3, ...     

, argsX); 

7.   【強制】方法參數在定義和傳入時,多個參數逗號後邊必須加空格。

正例:下例中實參的"a",後邊必需要有一個空格。

method("a", "b","c"); 

8.   【推薦】沒有必要增長若干空格來使某一行的字符與上一行的相應字符對齊。

正例:

int a = 3; 

long b = 4L; 

float c = 5F; 

StringBuffer sb = new StringBuffer(); 

說明:增長sb這個變量,若是須要對齊,則給a、b、c都要增長几個空格,在變量比較多的狀況下,是一種累贅的事情。

9. 【強制】IDE的text file encoding設置爲UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式。

10.【推薦】方法體內的執行語句組、變量的定義語句組、不一樣的業務邏輯之間或者不一樣的語義之間插入一個空行。相同業務邏輯和語義之間不須要插入空行。

說明:沒有必要插入多行空格進行隔開。

(四) OOP規約

1.   【強制】避免經過一個類的對象引用訪問此類的靜態變量或靜態方法,無謂增長編譯器解析成本,直接用類名來訪問便可。

2.   【強制】全部的覆寫方法,必須加@Override註解。

反例:getObject()與get0bject()的問題。一個是字母的O,一個是數字的0,加@Override能夠準確判斷是否覆蓋成功。另外,若是在抽象類中對方法簽名進行修改,其實現類會立刻編譯報錯。

3.   【強制】相同參數類型,相同業務含義,纔可使用Java的可變參數,避免使用Object。

說明:可變參數必須放置在參數列表的最後。(提倡同窗們儘可能不用可變參數編程)

正例:public User getUsers(Stringtype, Integer... ids);  

4.   【強制】對外暴露的接口簽名,原則上不容許修改方法簽名,避免對接口調用方產生影響。接口過期必須加@Deprecated註解,並清晰地說明採用的新接口或者新服務是什麼。

5.   【強制】不能使用過期的類或方法。

說明:java.net.URLDecoder 中的方法decode(StringencodeStr) 這個方法已通過時,應該使用雙參數decode(String source, Stringencode)。接口提供方既然明確是過期接口,那麼有義務同時提供新的接口;做爲調用方來講,有義務去考證過期方法的新實現是什麼。

6.   【強制】Object的equals方法容易拋空指針異常,應使用常量或肯定有值的對象來調用equals。

正例: "test".equals(object);

反例: object.equals("test");

說明:推薦使用java.util.Objects#equals (JDK7引入的工具類)

7.   【強制】全部的相同類型的包裝類對象之間值的比較,所有使用equals方法比較。

說明:對於Integer var=?在-128至127之間的賦值,Integer對象是在IntegerCache.cache 產生,會複用已有對象,這個區間內的Integer值能夠直接使用==進行判斷,可是這個區間以外的全部數據,都會在堆上產生,並不會複用已有對象,這是一個大坑,推薦使用equals方法進行判斷。

8.   【強制】關於基本數據類型與包裝數據類型的使用標準以下:

1) 全部的POJO類屬性必須使用包裝數據類型。

2) RPC方法的返回值和參數必須使用包裝數據類型。

3) 全部的局部變量推薦使用基本數據類型。

 說明:POJO類屬性沒有初值是提醒使用者在須要使用時,必須本身顯式地進行賦值,任何

NPE問題,或者入庫檢查,都由使用者來保證。

 正例:數據庫的查詢結果多是null,由於自動拆箱,用基本數據類型接收有NPE風險。  反例:某業務的交易報表上顯示成交總額漲跌狀況,即正負x%,x爲基本數據類型,調用的 RPC服務,調用不成功時,返回的是默認值,頁面顯示:0%,這是不合理的,應該顯示成中劃線-。因此包裝數據類型的null值,可以表示額外的信息,如:遠程調用失敗,異常退出。

9.   【強制】定義DO/DTO/VO等POJO類時,不要設定任何屬性默認值。

反例:某業務的DO的gmtCreate默認值爲newDate();可是這個屬性在數據提取時並無置入具體值,在更新其它字段時又附帶更新了此字段,致使建立時間被修改爲當前時間。

10.【強制】序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失敗;若是徹底不兼容升級,避免反序列化混亂,那麼請修改serialVersionUID值。

說明:注意serialVersionUID不一致會拋出序列化運行時異常。

11.【強制】構造方法裏面禁止加入任何業務邏輯,若是有初始化邏輯,請放在init方法中。

12.【強制】POJO類必須寫toString方法。使用工具類source> generate toString時,若是繼承了另外一個POJO類,注意在前面加一下super.toString。

說明:在方法執行拋出異常時,能夠直接調用POJO的toString()方法打印其屬性值,便於排查問題。

13. 【推薦】使用索引訪問用String的split方法獲得的數組時,需作最後一個分隔符後有無內容的檢查,不然會有拋IndexOutOfBoundsException的風險。

 

說明:

String str = "a,b,c,,"; String[] ary =str.split(","); 

//預期大於3,結果是3

System.out.println(ary.length); 

14.【推薦】當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一塊兒,便於閱讀。

15.【推薦】 類內方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter/setter方法。

說明:公有方法是類的調用者和維護者最關心的方法,首屏展現最好;保護方法雖然只是子類關心,也多是「模板設計模式」下的核心方法;而私有方法外部通常不須要特別關心,是一個黑盒實現;由於方法信息價值較低,全部Service和DAO的getter/setter方法放在類體最後。

16. 【推薦】setter方法中,參數名稱與類成員變量名稱一致,this.成員名=參數名。在 getter/setter方法中,儘可能不要增長業務邏輯,增長排查問題難度。

反例:

public IntegergetData(){      if(true)  {  return data +100; 

} else  { return data- 100;

 }  } 

17. 【推薦】循環體內,字符串的聯接方式,使用StringBuilder的append方法進行擴展。

反例:

String str ="start";      for(int i=0; i<100;i++){          str = str +"hello";     

說明:反編譯出的字節碼文件顯示每次循環都會new出一個StringBuilder對象,而後進行 append操做,最後經過toString方法返回String對象,形成內存資源浪費。

18.【推薦】final可提升程序響應效率,聲明成final的狀況:  1) 不須要從新賦值的變量,包括類屬性、局部變量。

2) 對象參數前加final,表示不容許修改引用的指向。

3)  類方法肯定不容許被重寫。

19.【推薦】慎用Object的clone方法來拷貝對象。

說明:對象的clone方法默認是淺拷貝,若想實現深拷貝須要重寫clone方法實現屬性對象的拷貝。

20.【推薦】類成員與方法訪問控制從嚴:

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的成員變量,刪除一下,不得手心冒點汗嗎?變量像本身的小孩,儘可能在本身的視線內,變量做用域太大,若是無限制的處處跑,那麼你會擔憂的。

(五) 集合處理

1.   【強制】Map/Set的key爲自定義對象時,必須重寫hashCode和equals。

正例:String重寫了hashCode和equals方法,因此咱們能夠很是愉快地使用String對象做爲key來使用。

2.   【強制】ArrayList的subList結果不可強轉成ArrayList,不然會拋出ClassCastException 異常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList; 說明:subList 返回的是 ArrayList 的內部類 SubList,並非 ArrayList ,而是 ArrayList 的一個視圖,對於SubList子列表的全部操做最終會反映到原列表上。

3.   【強制】在subList場景中,高度注意對原集合元素個數的修改,會致使子列表的遍歷、增長、刪除均產生ConcurrentModificationException 異常。

4.   【強制】使用集合轉數組的方法,必須使用集合的toArray(T[] array),傳入的是類型徹底同樣的數組,大小就是list.size()。

反例:直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強轉其它類型數組將出現ClassCastException錯誤。正例:

List<String> list = newArrayList<String>(2);     list.add("guan");     list.add("bao");      

String[] array = newString[list.size()];      array =list.toArray(array); 

說明:使用toArray帶參方法,入參分配的數組空間不夠大時,toArray方法內部將從新分配內存空間,並返回新數組地址;若是數組元素大於實際所需,下標爲[ list.size() ]的數組元素將被置爲null,其它數組元素保持原值,所以最好將方法入參數組大小定義與集合元素個數一致。

5.   【強制】使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。

說明:asList的返回對象是一個Arrays內部類,並無實現集合的修改方法。Arrays.asList 體現的是適配器模式,只是轉換接口,後臺的數據還是數組。

    String[] str = new String[] {"a", "b" };

    List list = Arrays.asList(str);

第一種狀況:list.add("c");運行時異常。第二種狀況:str[0]= "gujin"; 那麼list.get(0)也會隨之修改。

6.   【強制】泛型通配符<? extends T>來接收返回的數據,此寫法的泛型集合不能使用add方法。說明:蘋果裝箱後返回一個<? extends Fruits>對象,此對象就不能往裏加任何水果,包括蘋果。

7.   【強制】不要在foreach循環裏進行元素的remove/add操做。remove元素請使用Iterator 方式,若是併發操做,須要對Iterator對象加鎖。

反例:

List<String> a = newArrayList<String>();     

a.add("1");     

a.add("2");      for(String temp : a) {         if("1".equals(temp)){             

a.remove(temp);         

}     

說明:這個例子的執行結果會出乎你們的意料,那麼試一下把「1」換成「2」,會是一樣的結果嗎?正例:

Iterator<String> it = a.iterator(); while(it.hasNext()){             

String temp =  it.next();                       if(刪除元素的條件){                             it.remove();                

}     

}     

8. 【強制】在JDK7版本以上,Comparator要知足自反性,傳遞性,對稱性,否則Arrays.sort,

Collections.sort會報IllegalArgumentException異常。

說明:

1) 自反性:x,y的比較結果和y,x的比較結果相反。

2) 傳遞性:x>y,y>z,則x>z。

3) 對稱性:x=y,則x,z比較結果和y,z比較結果相同。反例:下例中沒有處理相等的狀況,實際使用中可能會出現異常:

new Comparator<Student>(){          

@Override         publicint compare(Student o1, Student o2){             return o1.getId() > o2.getId() ? 1 :-1;         }     

9. 【推薦】集合初始化時,儘可能指定集合初始值大小。說明:ArrayList儘可能使用ArrayList(int initialCapacity) 初始化。

10.【推薦】使用entrySet遍歷Map類集合KV,而不是keySet方式進行遍歷。

說明:keySet實際上是遍歷了2次,一次是轉爲Iterator對象,另外一次是從hashMap中取出key 所對應的value。而entrySet只是遍歷了一次就把key和value都放到了entry中,效率更高。若是是JDK8,使用Map.foreach方法。

正例:values()返回的是V值集合,是一個list集合對象;keySet()返回的是K值集合,是一個Set集合對象;entrySet()返回的是K-V值組合集合。

11.【推薦】高度注意Map類集合K/V能不能存儲null值的狀況,以下表格:

 

12.【參考】合理利用好集合的有序性(sort)和穩定性(order),避免集合的無序性(unsort)和不穩定性(unorder)帶來的負面影響。


說明:穩定性指集合每次遍歷的元素次序是必定的。有序性是指遍歷的結果是按某種比較規則依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是 order/sort。

13.【參考】利用Set元素惟一的特性,能夠快速對另外一個集合進行去重操做,避免使用List的 contains方法進行遍歷去重操做。

(六) 併發處理

1.   【強制】獲取單例對象要線程安全。在單例對象裏面作操做也要保證線程安全。

說明:資源驅動類、工具類、單例工廠類都須要注意。

2.   【強制】線程資源必須經過線程池提供,不容許在應用中自行顯式建立線程。

說明:使用線程池的好處是減小在建立和銷燬線程上所花的時間以及系統資源的開銷,解決資源不足的問題。若是不使用線程池,有可能形成系統建立大量同類線程而致使消耗完內存或者

「過分切換」的問題。

3.   【強制】SimpleDateFormat 是線程不安全的類,通常不要定義爲static變量,若是定義爲 static,必須加鎖,或者使用DateUtils工具類。

正例:注意線程安全,使用DateUtils。亦推薦以下處理:

private static final ThreadLocal<DateFormat> df =new ThreadLocal<DateFormat>() {      

 @Override       

protected DateFormat initialValue(){           

return newSimpleDateFormat("yyyy-MM-dd");       

}   

};   

說明:若是是JDK8的應用,可使用instant代替Date,Localdatetime代替Calendar,

Datetimeformatter代替Simpledateformatter,官方給出的解釋:simple beautifulstrong immutable thread-safe。

4.   【強制】高併發時,同步調用應該去考量鎖的性能損耗。能用無鎖數據結構,就不要用鎖;能鎖區塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。

5.   【強制】對多個資源、數據庫表、對象同時加鎖時,須要保持一致的加鎖順序,不然可能會形成死鎖。

說明:線程一須要對錶A、B、C依次所有加鎖後才能夠進行更新操做,那麼線程二的加鎖順序也必須是A、B、C,不然可能出現死鎖。

6.   【強制】併發修改同一記錄時,避免更新丟失,要麼在應用層加鎖,要麼在緩存加鎖,要麼在數據庫層使用樂觀鎖,使用version做爲更新依據。說明:若是每次訪問衝突機率小於20%,推薦使用樂觀鎖,不然使用悲觀鎖。樂觀鎖的重試次數不得小於3次。

7.   【強制】多線程並行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。

8.   【強制】線程池不容許使用Executors去建立,而是經過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors各個方法的弊端:

1)    newFixedThreadPool和newSingleThreadExecutor:  主要問題是堆積的請求處理隊列可能會耗費很是大的內存,甚至OOM。

2)    newCachedThreadPool和newScheduledThreadPool:  主要問題是線程數最大數是Integer.MAX_VALUE,可能會建立數量很是多的線程,甚至OOM。

9. 【強制】建立線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。

正例:

public class TimerTaskThread extends Thread{      publicTimerTaskThread(){         super.setName("TimerTaskThread"); … 

10.【推薦】使用CountDownLatch進行異步轉同步操做,每一個線程退出前必須調用countDown方法,線程執行代碼注意catch異常,確保countDown方法能夠執行,避免主線程沒法執行至 countDown方法,直到超時才返回結果。說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。

11.【推薦】避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 致使的性能降低。說明:Random實例包括java.util.Random 的實例或者 Math.random()實例。

正例:在JDK7以後,能夠直接使用API ThreadLocalRandom,在 JDK7以前,能夠作到每一個線程一個實例。

12. 【推薦】經過雙重檢查鎖(double-checked locking)(在併發場景)實現延遲初始化的優化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題解決方

案中較爲簡單一種(適用於jdk5及以上版本),將目標屬性聲明爲 volatile型(好比反例

中修改helper的屬性聲明爲private volatile Helper helper= null;);反例:

class Foo {  private Helper helper = null;  public Helper getHelper() { 

if (helper ==null)   synchronized(this) {      if (helper== null)         helper = newHelper();   

}      return helper; } 

// other functions and members... 

13.【參考】volatile解決多線程內存不可見問題。對於一寫多讀,是能夠解決變量同步問題,可是若是多寫,一樣沒法解決線程安全問題。若是想取回count++數據,使用以下類實現:

AtomicIntegercount = new AtomicInteger(); count.addAndGet(1); count++操做若是是

JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減小樂觀鎖的重試次數)。

14.【參考】注意HashMap的擴容死鏈,致使CPU飆升的問題。

15.【參考】ThreadLocal沒法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內全部操做共有的,因此設置爲靜態變量,全部此類實例共享此靜態變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,全部此類的對象(只要是這個線程內定義的)均可以操控這個變量。

(七) 控制語句

1.   【強制】在一個switch塊內,每一個case要麼經過break/return來終止,要麼註釋說明程序將繼續執行到哪個case爲止;在一個switch塊內,都必須包含一個default語句而且放在最後,即便它什麼代碼也沒有。

2.   【強制】在if/else/for/while/do語句中必須使用大括號,即便只有一行代碼,避免使用下面的形式:if (condition) statements;

3.    【推薦】推薦儘可能少用else, if-else的方式能夠改寫成:

if(condition){             …            return obj;    }  

// 接着寫else的業務邏輯代碼; 

說明:若是使用要if-else if-else方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式。

4.   【推薦】除經常使用方法(如getXxx/isXxx)等外,不要在條件判斷中執行復雜的語句,以提升可讀性。正例:

//僞代碼以下 

InputStream stream = file.open(fileName,"w"); 


if (stream != null) {

        …

}    反例:

if (file.open(fileName, "w") != null)){ 

5.   【推薦】循環體中的語句要考量性能,如下操做盡可能移至循環體外處理,如定義對象、變量、

獲取數據庫鏈接,進行沒必要要的try-catch操做(這個try-catch是否能夠移至循環體外)。

6.    【推薦】接口入參保護,這種場景常見的是用於作批量操做的接口。

7.   【參考】方法中須要進行參數校驗的場景:

1) 調用頻次低的方法。

2) 執行時間開銷很大的方法,參數校驗時間幾乎能夠忽略不計,但若是由於參數錯誤致使中間執行回退,或者錯誤,那得不償失。  3) 須要極高穩定性和可用性的方法。

 4) 對外提供的開放接口,無論是RPC/API/HTTP接口。

8.   【參考】方法中不須要參數校驗的場景:

1) 極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明裏必須註明外部參數檢查。

2) 底層的方法調用頻度都比較高,通常不校驗。畢竟是像純淨水過濾的最後一道,參數錯誤不太可能到底層纔會暴露問題。通常DAO層與Service層都在同一個應用中,部署在同一臺服務器中,因此DAO的參數校驗,能夠省略。

3)  被聲明成private只會被本身代碼所調用的方法,若是可以肯定調用方法的代碼傳入參數已經作過檢查或者確定不會有問題,此時能夠不校驗參數。

(八) 註釋規約

1.   【強制】類、類屬性、類方法的註釋必須使用javadoc規範,使用/**內容*/格式,不得使用

//xxx方式。

說明:在IDE編輯窗口中,javadoc方式會提示相關注釋,生成javadoc能夠正確輸出相應註釋;在IDE中,工程調用方法時,不進入方法便可懸浮提示方法、參數、返回值的意義,提升閱讀效率。

2.   【強制】全部的抽象方法(包括接口中的方法)必需要用javadoc註釋、除了返回值、參數、異常說明外,還必須指出該方法作什麼事情,實現什麼功能。

說明:若有實現和調用注意事項,請一併說明。

3.    【強制】全部的類都必須添加建立者信息。

4.   【強制】方法內部單行註釋,在被註釋語句上方另起一行,使用//註釋。方法內部多行註釋使用/* */註釋,注意與代碼對齊。

5.    【強制】全部的枚舉類型字段必需要有註釋,說明每一個數據項的用途。

6.   【推薦】與其「半吊子」英文來註釋,不如用中文註釋把問題說清楚。專有名詞、關鍵字,保持英文原文便可。

反例:「TCP鏈接超時」解釋成「傳輸控制協議鏈接超時」,理解反而費腦筋。

7.   【推薦】代碼修改的同時,註釋也要進行相應的修改,尤爲是參數、返回值、異常、核心邏輯等的修改。

說明:代碼與註釋更新不一樣步,就像路網與導航軟件更新不一樣步同樣,若是導航軟件嚴重滯後,就失去了導航的意義。

8.   【參考】註釋掉的代碼儘可能要配合說明,而不是簡單的註釋掉。

說明:代碼被註釋掉有兩種可能性:1)後續會恢復此段代碼邏輯。2)永久不用。前者若是沒有備註信息,難以知曉註釋動機。後者建議直接刪掉(代碼倉庫保存了歷史代碼)。

9.   【參考】對於註釋的要求:第1、可以準確反應設計思想和代碼邏輯;第2、可以描述業務含義,使別的程序員可以迅速瞭解到代碼背後的信息。徹底沒有註釋的大段代碼對於閱讀者形同天書,註釋是給本身看的,即便隔很長時間,也能清晰理解當時的思路;註釋也是給繼任者看的,使其可以快速接替本身的工做。

10.【參考】好的命名、代碼結構是自解釋的,註釋力求精簡準確、表達到位。避免出現註釋的一個極端:過多過濫的註釋,代碼的邏輯一旦修改,修改註釋是至關大的負擔。

反例:

// put elephant into fridge  put(elephant,fridge);   

  方法名put,加上兩個有意義的變量名elephant和fridge,已經說明了這是在幹什麼,語義清晰的代碼不須要額外的註釋。

11.【參考】特殊註釋標記,請註明標記人與標記時間。注意及時處理這些標記,經過標記掃描,常常清理此類標記。線上故障有時候就是來源於這些標記處的代碼。  1) 待辦事宜(TODO):( 標記人,標記時間,[預計處理時間])

   表示須要實現,但目前還未實現的功能。這其實是一個javadoc的標籤,目前的

javadoc尚未實現,但已經被普遍使用。只能應用於類,接口和方法(由於它是一個javadoc標籤)。

 2)錯誤,不能工做(FIXME):(標記人,標記時間,[預計處理時間])

   在註釋中用FIXME標記某代碼是錯誤的,並且不能工做,須要及時糾正的狀況。

 

(九) 其它

1.   【強制】在使用正則表達式時,利用好其預編譯功能,能夠有效加快正則匹配速度。

說明:不要在方法體內定義:Pattern pattern = Pattern.compile(規則);

2.   【強制】避免用Apache Beanutils進行屬性的copy。

說明:Apache BeanUtils性能較差,可使用其餘方案好比SpringBeanUtils, Cglib

BeanCopier。

3.   【強制】velocity調用POJO類的屬性時,建議直接使用屬性名取值便可,模板引擎會自動按規範調用POJO的getXxx(),若是是boolean基本數據類型變量(注意,boolean命名不須要加is前綴),會自動調用isXxx()方法。

說明:注意若是是Boolean包裝類對象,優先調用getXxx()的方法。

4.   【強制】後臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。

說明:若是var=null或者不存在,那麼${var}會直接顯示在頁面上。

5.   【強制】注意 Math.random() 這個方法返回是double類型,注意取值範圍 0≤x<1(可以取到零值,注意除零異常),若是想獲取整數類型的隨機數,不要將x放大10的若干倍而後取整,直接使用Random對象的nextInt或者nextLong方法。

6.   【強制】獲取當前毫秒數:System.currentTimeMillis(); 而不是new Date().getTime(); 說明:若是想獲取更加精確的納秒級時間值,用System.nanoTime。在JDK8中,針對統計時間等場景,推薦使用Instant類。

7.   【推薦】儘可能不要在vm中加入變量聲明、邏輯運算符,更不要在vm模板中加入任何複雜的邏輯。

8.   【推薦】任何數據結構的使用都應限制大小。

說明:這點很難徹底作到,但不少次的故障都是由於數據結構自增加,結果形成內存被吃光。

9.   【推薦】對於「明確中止使用的代碼和配置」,如方法、變量、類、配置文件、動態配置屬性等要堅定從程序中清理出去,避免形成過多垃圾。清理這類垃圾代碼是技術氣場,不要有這樣的觀念:「不作不錯,多作多錯」。

       

2、異常日誌

(一) 異常處理

1.    【強制】不要捕獲Java類庫中定義的繼承自RuntimeException的運行時異常類,如:

IndexOutOfBoundsException/ NullPointerException,這類異常由程序員預檢查來規避,保證程序健壯性。

正例:if(obj != null) {...}

反例:try { obj.method() }catch(NullPointerException e){…}

2.    【強制】異常不要用來作流程控制,條件控制,由於異常的處理效率比條件分支低。

3.    【強制】對大段代碼進行try-catch,這是不負責任的表現。catch時請分清穩定代碼和非穩定代碼,穩定代碼指的是不管如何不會出錯的代碼。對於非穩定代碼的catch儘量進行區分異常類型,再作對應的異常處理。

4.    【強制】捕獲異常是爲了處理它,不要捕獲了卻什麼都不處理而拋棄之,若是不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化爲用戶能夠理解的內容。

5.    【強制】有try塊放到了事務代碼中,catch異常後,若是須要回滾事務,必定要注意手動回滾事務。

6.    【強制】finally塊必須對資源對象、流對象進行關閉,有異常也要作try-catch。

說明:若是JDK7,可使用try-with-resources方法。

7.    【強制】不能在finally塊中使用return,finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句。

8.    【強制】捕獲異常與拋異常,必須是徹底匹配,或者捕獲異常是拋異常的父類。

說明:若是預期拋的是繡球,實際接到的是鉛球,就會產生意外狀況。

9.    【推薦】方法的返回值能夠爲null,不強制返回空集合,或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值。調用方須要進行null判斷防止NPE問題。

說明:本規約明確防止NPE是調用者的責任。即便被調用方法返回空集合或者空對象,對調用

者來講,也並不是高枕無憂,必須考慮到遠程調用失敗,運行時異常等場景返回null的狀況。

10.【推薦】防止NPE,是程序員的基本修養,注意NPE產生的場景:

1) 返回類型爲包裝數據類型,有多是null,返回int值時注意判空。

   反例:public int f(){return Integer對象},若是爲null,自動解箱拋NPE。

2) 數據庫的查詢結果可能爲null。

3) 集合裏的元素即便isNotEmpty,取出的數據元素也可能爲null。

4) 遠程調用返回對象,一概要求進行NPE判斷。

5) 對於Session中獲取的數據,建議NPE檢查,避免空指針。

6)  級聯調用obj.getA().getB().getC();一連串調用,易產生NPE。

11.【推薦】在代碼中使用「拋異常」仍是「返回錯誤碼」,對於公司外的http/api開放接口必須使用「錯誤碼」;而應用內部推薦異常拋出;跨應用間RPC調用優先考慮使用Result方式,封裝isSuccess、「錯誤碼」、「錯誤簡短信息」。

說明:關於RPC方法返回方式使用Result方式的理由:

1)   使用拋異常返回方式,調用方若是沒有捕獲到就會產生運行時錯誤。

2)   若是不加棧信息,只是new自定義異常,加入本身的理解的error message,對於調用端解決問題的幫助不會太多。若是加了棧信息,在頻繁調用出錯的狀況下,數據序列化和傳輸的性能損耗也是問題。

12.【推薦】定義時區分unchecked / checked 異常,避免直接使用RuntimeException拋出,更不容許拋出Exception或者Throwable,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如:DaoException / ServiceException等。

13.【參考】避免出現重複的代碼(Don’tRepeat Yourself),即DRY原則。

說明:隨意複製和粘貼代碼,必然會致使代碼的重複,在之後須要修改時,須要修改全部的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是共用模塊。

正例:一個類中有多個public方法,都須要進行數行相同的參數校驗操做,這個時候請抽取:

private boolean checkParam(DTO dto){...} 

(二) 日誌規約

1.   【強制】應用中不可直接使用日誌系統(Log4j、Logback)中的API,而應依賴使用日誌框架

SLF4J中的API,使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一。

import org.slf4j.Logger; 

import org.slf4j.LoggerFactory;

 private static final Logger logger =LoggerFactory.getLogger(Abc.class); 

2.    【強制】日誌文件推薦至少保存15天,由於有些異常具有以「周」爲頻次發生的特色。

3.   【強制】應用中的擴展日誌(如打點、臨時監控、訪問日誌等)命名方式:

appName_logType_logName.log。logType:日誌類型,推薦分類有stats/desc/monitor/visit 等;logName:日誌描述。這種命名的好處:經過文件名就可知道日誌文件屬於什麼應用,什麼


類型,什麼目的,也有利於歸類查找。

正例:mppserver應用中單獨監控時區轉換異常,如:                  mppserver_monitor_timeZoneConvert.log

說明:推薦對日誌進行分類,錯誤日誌和業務日誌儘可能分開存放,便於開發人員查看,也便於經過日誌對系統進行及時監控。

4.   【強制】對trace/debug/info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符的方式。

說明:logger.debug("Processingtrade with id: " + id + " symbol: " + symbol); 若是日誌級別是warn,上述日誌不會打印,可是會執行字符串拼接操做,若是symbol是對象,會執行toString()方法,浪費了系統資源,執行了上述操做,最終日誌卻沒有打印。

正例:(條件)

if (logger.isDebugEnabled()) {   

logger.debug("Processing trade with id: " +id + " symbol: " + symbol);  

}      

正例:(佔位符)

logger.debug("Processing trade with id: {} andsymbol : {} ", id, symbol); 

5.    【強制】避免重複打印日誌,浪費磁盤空間,務必在log4j.xml中設置additivity=false。

正例:<loggername="com.taobao.ecrm.member.config" additivity="false"> 

6.   【強制】異常信息應該包括兩類信息:案發現場信息和異常堆棧信息。若是不處理,那麼往上拋。

正例:logger.error(各種參數或者對象toString +"_" + e.getMessage(), e);

7.   輸出的POJO類必須重寫toString方法,不然只輸出此對象的hashCode值(地址值),沒啥參考意義。

8.   【推薦】可使用warn日誌級別來記錄用戶輸入參數錯誤的狀況,避免用戶投訴時,無所適從。注意日誌輸出的級別,error級別只記錄系統邏輯出錯、異常、或者重要的錯誤信息。如非必要,請不要在此場景打出error級別,避免頻繁報警。

9.   【推薦】謹慎地記錄日誌。生產環境禁止輸出debug日誌;有選擇地輸出info日誌;若是使用warn來記錄剛上線時的業務行爲信息,必定要注意日誌輸出量的問題,避免把服務器磁盤撐爆,並記得及時刪除這些觀察日誌。

說明:大量地輸出無效日誌,不利於系統性能提高,也不利於快速定位錯誤點。紀錄日誌時請思考:這些日誌真的有人看嗎?看到這條日誌你能作什麼?能不能給問題排查帶來好處?

10.【參考】若是日誌用英文描述不清楚,推薦使用中文註釋。對於中文UTF-8的日誌,在secureCRT 中,set encoding=utf-8;若是中文字符還亂碼,請設置:全局>默認的會話設置>外觀>字體> 選擇字符集gb2312;若是還不行,執行命令:set termencoding=gbk,而且直接使用中文來進行檢索。

3、MYSQL規約

(一) 建表規約

1.   【強制】表達是與否概念的字段,必須使用is_xxx的方式命名,數據類型是unsigned tinyint

( 1表示是,0表示否),此規則一樣適用於odps建表。

說明:任何字段若是爲非負數,必須是unsigned。

2.   【強制】表名、字段名必須使用小寫字母或數字;禁止出現數字開頭,禁止兩個下劃線中間只出現數字。數據庫字段名的修改代價很大,由於沒法進行預發佈,因此字段名稱須要慎重考慮。

正例:getter_admin,task_config,level3_name 反例:GetterAdmin,taskConfig,level_3_name

3.   【強制】表名不使用複數名詞。

說明:表名應該僅僅表示表裏面的實體內容,不該該表示實體數量,對應於DO類名也是單數形式,符合表達習慣。

4.    【強制】禁用保留字,如desc、range、match、delayed等,參考官方保留字。

5.   【強制】惟一索引名爲uk_字段名;普通索引名則爲idx_字段名。

說明:uk_即 unique key;idx_ 即index的簡稱。

6.   【強制】小數類型爲decimal,禁止使用float和double。

說明:float和double在存儲的時候,存在精度損失的問題,極可能在值的比較時,獲得不

正確的結果。若是存儲的數據範圍超過decimal的範圍,建議將數據拆成整數和小數分開存儲。

7.    【強制】若是存儲的字符串長度幾乎相等,使用CHAR定長字符串類型。

8.   【強制】varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000,若是存儲長度大於此值,定義字段類型爲TEXT,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。

9.   【強制】表必備三字段:id, gmt_create, gmt_modified。

說明:其中id必爲主鍵,類型爲unsigned bigint、單表時自增、步長爲1;分表時改成從

TDDL Sequence取值,確保分表之間的全局惟一。gmt_create,gmt_modified的類型均爲 date_time類型。

10.【推薦】表的命名最好是加上「業務名稱_表的做用」,避免上雲梯後,再與其它業務表關聯時有混淆。

正例:tiger_task/ tiger_reader / mpp_config

11.【推薦】庫名與應用名稱儘可能一致。

12.【推薦】若是修改字段含義或對字段表示的狀態追加時,須要及時更新字段註釋。

13.【推薦】字段容許適當冗餘,以提升性能,可是必須考慮數據同步的狀況。冗餘字段應遵循:

1)   不是頻繁修改的字段。

2)   不是varchar超長字段,更不能是text字段。

正例:各業務線常常冗餘存儲商品名稱,避免查詢時須要調用IC服務獲取。

14.【推薦】單錶行數超過500萬行或者單表容量超過2GB,才推薦進行分庫分表。

說明:若是預計三年後的數據量根本達不到這個級別,請不要在建立表時就分庫分表。

反例:某業務三年總數據量才2萬行,卻分紅1024張表,問:你爲何這麼設計?答:分1024 張表,不是標配嗎?

15.【參考】合適的字符存儲長度,不但節約數據庫表空間、節約索引存儲,更重要的是提高檢索速度。

正例:人的年齡用unsigned tinyint(表示範圍0-255,人的壽命不會超過255歲);海龜就必須是smallint,但若是是太陽的年齡,就必須是int;若是是全部恆星的年齡都加起來,那麼就必須使用bigint。

(二) 索引規約

1.   【強制】業務上具備惟一特性的字段,即便是組合字段,也必須建成惟一索引。

說明:不要覺得惟一索引影響了insert速度,這個速度損耗能夠忽略,但提升查找速度是明顯的;另外,即便在應用層作了很是完善的校驗和控制,只要沒有惟一索引,根據墨菲定律,必然有髒數據產生。

2.   【強制】超過三個表禁止join。須要join的字段,數據類型保持絕對一致;多表關聯查詢時,保證被關聯的字段須要有索引。

說明:即便雙表join也要注意表索引、SQL性能。

3.   【強制】在varchar字段上創建索引時,必須指定索引長度,不必對全字段創建索引,根據實際文本區分度決定索引長度。

說明:索引的長度與區分度是一對矛盾體,通常對字符串類型數據,長度爲20的索引,區分度會高達90%以上,可使用count(distinct left(列名, 索引長度))/count(*)的區分度來肯定。

4.   【強制】頁面搜索嚴禁左模糊或者全模糊,若是須要請走搜索引擎來解決。

說明:索引文件具備B-Tree的最左前綴匹配特性,若是左邊的值未肯定,那麼沒法使用此索引。

5.   【推薦】若是有order by的場景,請注意利用索引的有序性。order by 最後的字段是組合索引的一部分,而且放在索引組合順序的最後,避免出現file_sort的狀況,影響查詢性能。

正例:where a=? and b=? order by c; 索引:a_b_c

反例:索引中有範圍查找,那麼索引有序性沒法利用,如:WHERE a>10 ORDER BY b; 索引a_b 沒法排序。

6.   【推薦】利用覆蓋索引來進行查詢操做,來避免回表操做。

說明:若是一本書須要知道第11章是什麼標題,會翻開第11章對應的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的做用。

正例:IDB可以創建索引的種類:主鍵索引、惟一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結果,extra列會出現:using index.

7.   【推薦】利用延遲關聯或者子查詢優化超多分頁場景。

說明:MySQL並非跳過offset行,而是取offset+N行,而後返回放棄前offset行,返回N 行,那當offset特別大的時候,效率就很是的低下,要麼控制返回的總頁數,要麼對超過特定閾值的頁數進行SQL改寫。

正例:先快速定位須要獲取的id段,而後再關聯:

      SELECT a.* FROM 表1 a, (select id from表1 where 條件 LIMIT 100000,20 ) bwhere a.id=b.id

8.   【推薦】 SQL性能優化的目標:至少要達到range 級別,要求是ref級別,若是能夠是consts 最好。說明:

1)   consts 單表中最多隻有一個匹配行(主鍵或者惟一索引),在優化階段便可讀取到數據。

2)   ref 指的是使用普通的索引。(normalindex)

3)   range 對索引進範圍檢索。

反例:explain表的結果,type=index,索引物理文件全掃描,速度很是慢,這個index級別比較range還低,與全表掃描是小巫見大巫。

9.   【推薦】建組合索引的時候,區分度最高的在最左邊。

正例:若是where a=? and b=? ,a列的幾乎接近於惟一值,那麼只須要單建idx_a索引便可。說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>?

and b=? 那麼即便a的區分度更高,也必須把b放在索引的最前列。

10.【參考】建立索引時避免有以下極端誤解:

1)   誤認爲一個查詢就須要建一個索引。

2)   誤認爲索引會消耗空間、嚴重拖慢更新和新增速度。

3)    誤認爲惟一索引一概須要在應用層經過「先查後插」方式解決。

(三) SQL規約

1.   【強制】不要使用count(列名)或count(常量)來替代count(*),count(*)就是SQL92定義的標準統計行數的語法,跟數據庫無關,跟NULL和非NULL無關。

說明:count(*)會統計值爲NULL的行,而count(列名)不會統計此列爲NULL值的行。

2.   【強制】count(distinct col) 計算該列除NULL以外的不重複數量。注意 count(distinct col1, col2) 若是其中一列全爲NULL,那麼即便另外一列有不一樣的值,也返回爲0。

3.   【強制】當某一列的值全是NULL時,count(col)的返回結果爲0,但sum(col)的返回結果爲

NULL,所以使用sum()時需注意NPE問題。

正例:可使用以下方式來避免sum的NPE問題:SELECTIF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

4.   【強制】使用ISNULL()來判斷是否爲NULL值。注意:NULL與任何值的直接比較都爲NULL。

說明:

1)    NULL<>NULL的返回結果是NULL,不是false。

2)    NULL=NULL的返回結果是NULL,不是true。

3)    NULL<>1的返回結果是NULL,而不是true。

5.    【強制】在代碼中寫分頁查詢邏輯時,若count爲0應直接返回,避免執行後面的分頁語句。

6.   【強制】不得使用外鍵與級聯,一切外鍵概念必須在應用層解決。

說明:(概念解釋)學生表中的student_id是主鍵,那麼成績表中的student_id則爲外鍵。

若是更新學生表中的student_id,同時觸發成績表中的student_id更新,則爲級聯更新。外鍵與級聯更新適用於單機低併發,不適合分佈式、高併發集羣;級聯更新是強阻塞,存在數據庫更新風暴的風險;外鍵影響數據庫的插入速度。

7.   【強制】禁止使用存儲過程,存儲過程難以調試和擴展,更沒有移植性。

8.   【強制】IDB數據訂正時,刪除和修改記錄時,要先select,避免出現誤刪除,確認無誤才能提交執行。

9.   【推薦】in操做能避免則避免,若實在避免不了,須要仔細評估in後邊的集合元素數量,控制在1000個以內。

10.【參考】因阿里巴巴全球化須要,全部的字符存儲與表示,均以utf-8編碼,那麼字符計數方法注意:

說明:

   SELECT LENGTH("阿里巴巴"); 返回爲12

   SELECT CHARACTER_LENGTH("阿里巴巴"); 返回爲4

    若是要使用表情,那麼使用utfmb4來進行存儲,注意它與utf-8編碼。

11.【參考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系統和事務日誌資源少,但TRUNCATE 無事務且不觸發trigger,有可能形成事故,故不建議在開發代碼中使用此語句。

說明:TRUNCATETABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。

(四) ORM規約

1.   【強制】在表查詢中,一概不要使用 * 做爲查詢的字段列表,須要哪些字段必須明確寫明。

說明:1)增長查詢分析器解析成本。2)增減字段容易與resultMap配置不一致。

2.   【強制】POJO類的boolean屬性不能加is,而數據庫字段必須加is_,要求在resultMap中進行字段與屬性之間的映射。

說明:參見定義POJO類以及數據庫字段定義規定,在sql.xml增長映射,是必須的。

3.   【強制】不要用resultClass當返回參數,即便全部類屬性名與數據庫字段一一對應,也須要定義;反過來,每個表也必然有一個與之對應。

說明:配置映射關係,使字段與DO類解耦,方便維護。

4.    【強制】xml配置中參數注意使用:#{},#param#不要使用${} 此種方式容易出現SQL注入。

5.   【強制】iBATIS自帶的queryForList(StringstatementName,int start,int size)不推薦使用。

說明:其實現方式是在數據庫取到statementName對應的SQL語句的全部記錄,再經過subList 取start,size的子集合,線上由於這個緣由曾經出現過OOM。

 

 


正例:在sqlmap.xml中引入 #start#, #size#

Map<String, Object> map = new HashMap<String,Object>();    map.put("start",start);    map.put("size", size);   

6.   【強制】不容許直接拿HashMap與HashTable做爲查詢結果集的輸出。

反例:某同窗爲避免寫一個<resultMap>,直接使用HashTable來接收數據庫返回結果,結果出現平常是把bigint轉成Long值,而線上因爲數據庫版本不同,解析成BigInteger,致使線上問題。

7.    【強制】更新數據表記錄時,必須同時更新記錄對應的gmt_modified字段值爲當前時間。

8.   【推薦】不要寫一個大而全的數據更新接口,傳入爲POJO類,無論是否是本身的目標更新字段,都進行update table set c1=value1,c2=value2,c3=value3; 這是不對的。執行SQL時,儘可能不要更新無改動的字段,一是易出錯;二是效率低;三是binlog增長存儲。

9.   【參考】@Transactional事務不要濫用。事務會影響數據庫的QPS,另外使用事務的地方須要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統計修正等。

10.【參考】<isEqual>中的compareValue是與屬性值對比的常量,通常是數字,表示相等時帶上此條件;<isNotEmpty>表示不爲空且不爲null時執行;<isNotNull>表示不爲null值時執行。

4、工程規約

(一) 應用分層

1. 【推薦】圖中默認上層依賴於下層,箭頭關係表示可直接依賴,如:開放接口層能夠依賴於

Web層,也能夠直接依賴於Service層,依此類推:

 

•       開放接口層:可直接封裝Service接口暴露成RPC接口;經過Web封裝成http接口;網關控制層等。

•       終端顯示層:各個端的模板渲染並執行顯示層。當前主要是velocity渲染,JS渲染,JSP渲染,移動端展現層等。

•       Web層:主要是對訪問控制進行轉發,各種基本參數校驗,或者不復用的業務簡單處理等。

•       Service層:相對具體的業務邏輯服務層。

•       Manager層:通用業務處理層,它有以下特徵:

1) 對第三方平臺封裝的層,預處理返回結果及轉化異常信息;

2) 對Service層通用能力的下沉,如緩存方案、中間件通用處理;

3)  與DAO層交互,對DAO的業務通用能力的封裝。

•       DAO層:數據訪問層,與底層Mysql、Oracle、Hbase、OB進行數據交互。

•       外部接口或第三方平臺:包括其它部門RPC開放接口,基礎平臺,其它公司的HTTP接口。

2.   【參考】(分層異常處理規約)在DAO層,產生的異常類型有不少,沒法用細粒度異常進行

catch,使用catch(Exception e)方式,並throw newDaoException(e),不須要打印日誌,

由於日誌在Manager/Service層必定須要捕獲並打到日誌文件中去,若是同臺服務器再打日誌,浪費性能和存儲。在Service層出現異常時,必須記錄日誌信息到磁盤,儘量帶上參數信息,至關於保護案發現場。若是Manager層與Service同機部署,日誌方式與DAO層處理一致,若是是單獨部署,則採用與Service一致的處理方式。Web層毫不應該繼續往上拋異常,由於已經處於頂層,無繼續處理異常的方式,若是意識到這個異常將致使頁面沒法正常渲染,那麼就應該直接跳轉到友好錯誤頁面,儘可能加上友好的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。

3.    【參考】分層領域模型規約:

•       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.tddl 或 com.alibaba.sourcing.multilang

2)    ArtifactID格式:產品線名-模塊名。語義不重複不遺漏,先到倉庫中心去查證一下。

  正例:tc-client / uic-api / tair-tool

3)     Version:詳細規定參考下方。

2.   【強制】二方庫版本號命名方式:主版本號.次版本號.修訂號

1)    主版本號:當作了不兼容的API 修改,或者增長了能改變產品方向的新功能。

2)    次版本號:當作了向下兼容的功能性新增(新增類、接口等)。

3)     修訂號:修復bug,沒有修改方法簽名的功能增強,保持 API 兼容性。

3.   【強制】線上應用不要依賴SNAPSHOT版本(安全包除外);正式發佈的類庫必須使用RELEASE 版本號升級+1的方式,且版本號不容許覆蓋升級,必須去中央倉庫進行查證。

說明:不依賴SNAPSHOT版本是保證應用發佈的冪等性。另外,也能夠加快編譯時的打包構建。

4.   【強制】二方庫的新增或升級,保持除功能點以外的其它jar包仲裁結果不變。若是有改變,必須明確評估和驗證,建議進行dependency:resolve先後信息比對,若是仲裁結果徹底不一致,那麼經過dependency:tree命令,找出差別點,進行<excludes>排除jar包。

5.   【強制】二方庫裏能夠定義枚舉類型,參數可使用枚舉類型,可是接口返回值不容許使用枚舉類型或者包含枚舉類型的POJO對象。

6.   【強制】依賴於一個二方庫羣時,必須定義一個統一版本變量,避免版本號不一致。

說明:依賴springframework-core,-context,-beans,它們都是同一個版本,能夠定義一個變量來保存版本:${spring.version},定義依賴的時候,引用該版本。

7.   【強制】禁止在子項目的pom依賴中出現相同的GroupId,相同的ArtifactId,可是不一樣的

Version。

說明:在本地調試時會使用各子項目指定的版本號,可是合併成一個war,只能有一個版本號出如今最後的lib目錄中。曾經出現過線下調試是正確的,發佈到線上出故障的先例。

8.    【推薦】工具類二方庫已經提供的,儘可能不要在本應用中編程實現。

l  json操做: fastjson


l md5操做:commons-codec

l 工具集合:Guava包

l 數組操做:ArrayUtils(org.apache.commons.lang3.ArrayUtils)

l 集合操做:CollectionUtils(org.apache.commons.collections4.CollectionUtils)

l  除上面之外還有NumberUtils、DateFormatUtils、DateUtils等優先使用 org.apache.commons.lang3這個包下的,不要使用org.apache.commons.lang包下面的。緣由是commons.lang這個包是從JDK1.2開始支持的因此不少1.5/1.6的特性是不支持的,例如:泛型。

9.   【推薦】全部pom文件中的依賴聲明放在<dependencies>語句塊中,全部版本仲裁放在

<dependencyManagement>語句塊中。

說明:<dependencyManagement>裏只是聲明版本,並不實現引入,所以子項目須要顯式的聲明

依賴,version和scope都讀取自父pom。而<dependencies>全部聲明在主pom的<dependencies > 裏的依賴都會自動引入,並默認被全部的子項目繼承。

10.【推薦】二方庫儘可能不要有配置項,最低限度不要再增長配置項。

11.【參考】爲避免應用二方庫的依賴衝突問題,二方庫發佈者應當遵循如下原則:

1)    精簡可控原則。移除一切沒必要要的API和依賴,只包含 Service API、必要的領域模型對象、Utils類、常量、枚舉等。若是依賴其它二方庫,儘可能是provided引入,讓二方庫使用者去依賴具體版本號;無log具體實現,只依賴日誌框架。

2)    穩定可追溯原則。每一個版本的變化應該被記錄,二方庫由誰維護,源碼在哪裏,都須要能方便查到。除非用戶主動升級版本,不然公共二方庫的行爲不該該發生變化。

(三) 服務器規約

1.   【推薦】高併發服務器建議調小TCP協議的time_wait超時時間。

說明:操做系統默認240秒後,纔會關閉處於time_wait狀態的鏈接,在高併發訪問下,服務器端會由於處於time_wait的鏈接數太多,可能沒法創建新的鏈接,因此須要在服務器上調小此等待值。

正例:在linux服務器上請經過變動/etc/sysctl.conf文件去修改該缺省值(秒):     net.ipv4.tcp_fin_timeout = 30

2.   【推薦】調大服務器所支持的最大文件句柄數(File Descriptor,簡寫爲fd)。

說明:主流操做系統的設計是將TCP/UDP鏈接採用與文件同樣的方式去管理,即一個鏈接對應於一個fd。主流的linux服務器默認所支持最大fd數量爲1024,當併發鏈接數很大時很容易由於fd不足而出現「open too many files」錯誤,致使新的鏈接沒法創建。建議將linux 服務器所支持的最大句柄數調高數倍(與服務器的內存數量相關)。

3.   【推薦】給JVM設置-XX:+HeapDumpOnOutOfMemoryError參數,讓JVM碰到OOM場景時輸出dump 信息。

說明:OOM的發生是有機率的,甚至有規律地相隔數月纔出現一例,出現時的現場信息對查錯很是有價值。

4.   【參考】服務器內部重定向必須使用forward;外部重定向地址必須使用URL Broker生成,不然因線上採用HTTPS協議而致使瀏覽器提示「不安全」。此外,還會帶來URL維護不一致的問題。

    

5、安全規約

1.   【強制】可被用戶直接訪問的功能必須進行權限控制校驗。說明:防止沒有作權限控制就可隨意訪問、操道別人的數據,好比查看、修改別人的訂單。

2.   【強制】用戶敏感數據禁止直接展現,必須對展現數據脫敏。說明:支付寶中查看我的手機號碼會顯示成:158****9119,隱藏中間4位,防止隱私泄露。

3.   【強制】用戶輸入的SQL參數嚴格使用參數綁定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL訪問數據庫。

4.   【強制】用戶請求傳入的任何參數必須作有效性驗證。說明:忽略參數校驗可能致使:

l page size過大致使內存溢出

l 惡意order by致使數據庫慢查詢

l 正則輸入源串拒絕服務ReDOS

l 任意重定向

l SQL注入

l Shell注入

l 反序列化注入

5.    【強制】禁止向HTML頁面輸出未經安全過濾或未正確轉義的用戶數據。

6.   【強制】表單、AJAX提交必須執行CSRF安全過濾。

說明:CSRF(Cross-siterequest forgery)跨站請求僞造是一類常見編程漏洞。對於存在CSRF

漏洞的應用/網站,攻擊者能夠事先構造好URL,只要受害者用戶一訪問,後臺便在用戶不知情狀況下對數據庫中用戶參數進行相應修改。

7.   【強制】URL外部重定向傳入的目標地址必須執行白名單過濾。

正例:

try{         if(com.alibaba.fasttext.sec.url.CheckSafeUrl             

.getDefaultInstance().inWhiteList(targetUrl)){                 response.sendRedirect(targetUrl);        

  }     

} catch (IOException e){            logger.error("Check returnURL error! targetURL=" + targetURL,e);          throwe;     

8.    【強制】Web應用必須正確配置Robots文件,非SEO URL必須配置爲禁止爬蟲訪問。

User-agent: * Disallow: / 

9.   【強制】在使用平臺資源,譬如短信、郵件、電話、下單、支付,必須實現正確的防重放限制,如數量限制、疲勞度控制、驗證碼校驗,避免被濫刷、資損。

說明:如註冊時發送驗證碼到手機,若是沒有限制次數和頻率,那麼能夠利用此功能騷擾到其它用戶,並形成短信平臺資源浪費。

10.【推薦】發貼、評論、發送即時消息等用戶生成內容的場景必須實現防刷、文本內容違禁詞過濾等風控策略。

相關文章
相關標籤/搜索