JAVA開發(fā)中的一些規(guī)范講解(阿里巴巴Java開發(fā)規(guī)范手冊)
一、編程規(guī)約
(一) 命名規(guī)約
1. 【強制】所有編程相關(guān)命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結(jié)束。反例: _name / __name / $Object / name_ / name$ / Object$
2. 【強制】所有編程相關(guān)的命名嚴(yán)禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。說明:正確的英文拼寫和語法可以讓閱讀者易于理解,避免歧義。注意,即使純拼音命名方式也要避免采用。
反例: DaZhePromotion [打折] / getPingfenByName() [評分] / int 變量 = 3; 正例: ali / alibaba / taobao /cainiao / aliyun / youku / hangzhou 等國際通用的名稱,可視為英文。
3. 【強制】類名使用UpperCamelCase風(fēng)格,必須遵從駝峰形式,但以下情形例外:(領(lǐng)域模型的相關(guān)命名)DO / DTO / VO / DAO等。
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 反例:macroPolo / UserDo /XMLService / TCPUDPDeal / TAPromotion
4. 【強制】方法名、參數(shù)名、成員變量、局部變量都統(tǒng)一使用lowerCamelCase風(fēng)格,必須遵從駝峰形式。
正例:localValue / getHttpMessage() / inputUserId
5. 【強制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達(dá)完整清楚,不要嫌名字長。正例: MAX_STOCK_COUNT 反例: MAX_COUNT
6. 【強制】抽象類命名使用Abstract或Base開頭;異常類命名使用Exception結(jié)尾;測試類命名以它要測試的類的名稱開始,以Test結(jié)尾。
7. 【強制】中括號是數(shù)組類型的一部分,數(shù)組定義如下:String[] args; 反例:請勿使用String args[]的方式來定義
8. 【強制】POJO類中的任何布爾類型的變量,都不要加is,否則部分框架解析會引起序列化錯誤。
反例:定義為基本數(shù)據(jù)類型boolean isSuccess;的屬性,它的方法也是isSuccess(),RPC
框架在反向解析的時候,“以為”對應(yīng)的屬性名稱是success,導(dǎo)致屬性獲取不到,進(jìn)而拋出異常。
9. 【強制】包名統(tǒng)一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統(tǒng)一使用單數(shù)形式,但是類名如果有復(fù)數(shù)含義,類名可以使用復(fù)數(shù)形式。
正例: 應(yīng)用工具類包名為com.alibaba.mpp.util、類名為MessageUtils(此規(guī)則參考spring 的框架結(jié)構(gòu))
10.【強制】杜絕完全不規(guī)范的縮寫,避免望文不知義。
反例:<某業(yè)務(wù)代碼>AbstractClass“縮寫”命名成AbsClass;condition“縮寫”命名成 condi,此類隨意縮寫嚴(yán)重降低了代碼的可閱讀性。
11.【推薦】如果使用到了設(shè)計模式,建議在類名中體現(xiàn)出具體模式。
說明:將設(shè)計模式體現(xiàn)在名字中,有利于閱讀者快速理解架構(gòu)設(shè)計思想。
正例:public class OrderFactory; public class LoginProxy;
public classResourceObserver;
12.【推薦】接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,并加上有效的javadoc注釋。盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關(guān),并且是整個應(yīng)用的基礎(chǔ)常量。
正例:接口方法簽名:void f();
接口基礎(chǔ)常量表示:String COMPANY = "alibaba";
反例:接口方法定義:public abstract void f();
說明:JDK8中接口允許有默認(rèn)實現(xiàn),那么這個default方法,是對所有實現(xiàn)類都有價值的默認(rèn)實現(xiàn)。
13.接口和實現(xiàn)類的命名有兩套規(guī)則:
1) 【強制】對于Service和DAO類,基于SOA的理念,暴露出來的服務(wù)一定是接口,內(nèi)部的實現(xiàn)類用Impl的后綴與接口區(qū)別。
正例:CacheServiceImpl實現(xiàn)CacheService接口。
2) 【推薦】 如果是形容能力的接口名稱,取對應(yīng)的形容詞做接口名(通常是–able的形式)。
正例:AbstractTranslator實現(xiàn) Translatable。
14.【參考】枚舉類名建議帶上Enum后綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。
說明:枚舉其實就是特殊的常量類,且構(gòu)造方法被默認(rèn)強制是私有。
正例:枚舉名字:DealStatusEnum;成員名稱:SUCCESS / UNKOWN_REASON。
15.【參考】各層命名規(guī)約:
A) Service/DAO層方法命名規(guī)約
1) 獲取單個對象的方法用get做前綴。
2) 獲取多個對象的方法用list做前綴。
3) 獲取統(tǒng)計值的方法用count做前綴。
4) 插入的方法用save(推薦)或insert做前綴。
5) 刪除的方法用remove(推薦)或delete做前綴。
6) 修改的方法用update做前綴。
B) 領(lǐng)域模型命名規(guī)約
1) 數(shù)據(jù)對象:xxxDO,xxx即為數(shù)據(jù)表名。
2) 數(shù)據(jù)傳輸對象:xxxDTO,xxx為業(yè)務(wù)領(lǐng)域相關(guān)的名稱。
3) 展示對象:xxxVO,xxx一般為網(wǎng)頁名稱。
4) POJO是DO/DTO/BO/VO的統(tǒng)稱,禁止命名成xxxPOJO。
(二) 常量定義
1. 【強制】不允許出現(xiàn)任何魔法值(即未經(jīng)定義的常量)直接出現(xiàn)在代碼中。
反例: String key="Id#taobao_"+tradeId; cache.put(key, value);
2. 【強制】long或者Long初始賦值時,必須使用大寫的L,不能是小寫的l,小寫容易跟數(shù)字1 混淆,造成誤解。
說明:Longa = 2l; 寫的是數(shù)字的21,還是Long型的2?
3. 【推薦】不要使用一個常量類維護所有常量,應(yīng)該按常量功能進(jìn)行歸類,分開維護。如:緩存相關(guān)的常量放在類:CacheConsts下;系統(tǒng)配置相關(guān)的常量放在類:ConfigConsts下。
說明:大而全的常量類,非得ctrl+f才定位到修改的常量,不利于理解,也不利于維護。
4. 【推薦】常量的復(fù)用層次有五層:跨應(yīng)用共享常量、應(yīng)用內(nèi)共享常量、子工程內(nèi)共享常量、包內(nèi)共享常量、類內(nèi)共享常量。
1) 跨應(yīng)用共享常量:放置在二方庫中,通常是client.jar中的const目錄下。
2) 應(yīng)用內(nèi)共享常量:放置在一方庫的modules中的const目錄下。
反例:易懂變量也要統(tǒng)一定義成應(yīng)用內(nèi)共享常量,兩位攻城師在兩個類中分別定義了表示
“是”的變量:
類A中:public static final String YES ="yes"; 類B中:public static final String YES = "y";
A.YES.equals(B.YES),預(yù)期是true,但實際返回為false,導(dǎo)致產(chǎn)生線上問題。
3) 子工程內(nèi)部共享常量:即在當(dāng)前子工程的const目錄下。
4) 包內(nèi)共享常量:即在當(dāng)前包下單獨的const目錄下。
5) 類內(nèi)共享常量:直接在類內(nèi)部private static final定義。
5. 【推薦】如果變量值僅在一個范圍內(nèi)變化用Enum類。如果還帶有名稱之外的延伸屬性,必須使用Enum類,下面正例中的數(shù)字就是延伸信息,表示星期幾。
正例:public Enum{ MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4),FRIDAY(5),
SATURDAY(6), SUNDAY(7);}
(三) 格式規(guī)約
1. 【強制】大括號的使用約定。如果是大括號內(nèi)為空,則簡潔地寫成{}即可,不需要換行;如果是非空代碼塊則:
1) 左大括號前不換行。
2) 左大括號后換行。
3) 右大括號前換行。
4) 右大括號后還有else等代碼則不換行;表示終止右大括號后必須換行。
2. 【強制】 左括號和后一個字符之間不出現(xiàn)空格;同樣,右括號和前一個字符之間也不出現(xiàn)空格。詳見第5條下方正例提示。
3. 【強制】if/for/while/switch/do等保留字與左右括號之間都必須加空格。
4. 【強制】任何運算符左右必須加一個空格。
說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號、三目運行符等。
5. 【強制】代碼塊縮進(jìn)4個空格,如果使用tab縮進(jìn),請設(shè)置成1個tab為4個空格。
正例:(涉及1-5點)
public static void main(String args[]){
// 縮進(jìn)4個空格
String say ="hello";
// 運算符的左右必須有一個空格
int flag = 0;
// 關(guān)鍵詞if與括號之間必須有一個空格,括號內(nèi)f與左括號,1與右括號不需要空格 if(flag == 0) {
System.out.println(say);
}
// 左大括號前加空格且不換行;左大括號后換行
if (flag == 1){
System.out.println("world");
// 右大括號前換行,右大括號后有else,不用換行
} else{
System.out.println("ok");
// 右大括號做為結(jié)束,必須換行
}
}
6.【強制】單行字符數(shù)限制不超過120個,超出需要換行,換行時,遵循如下原則: 1) 換行時相對上一行縮進(jìn)4個空格。
2) 運算符與下文一起換行。
3) 方法調(diào)用的點符號與下文一起換行。
4) 在多個參數(shù)超長,逗號后進(jìn)行換行。
5) 在括號前不要換行,見反例。正例:
StringBuffer sb = new StringBuffer();
//超過120個字符的情況下,換行縮進(jìn)4個空格,并且方法前的點符號一起換行 sb.append("zi").append("xin")…
.append("huang");
反例:
StringBuffer sb = new StringBuffer();
//超過120個字符的情況下,不要在括號前換行
sb.append("zi").append("xin")…append
("huang");
//參數(shù)很多的方法調(diào)用也超過120個字符,逗號后才是換行處 method(args1,args2, args3, ...
, argsX);
7. 【強制】方法參數(shù)在定義和傳入時,多個參數(shù)逗號后邊必須加空格。
正例:下例中實參的"a",后邊必須要有一個空格。
method("a", "b","c");
8. 【推薦】沒有必要增加若干空格來使某一行的字符與上一行的相應(yīng)字符對齊。
正例:
int a = 3;
long b = 4L;
float c = 5F;
StringBuffer sb = new StringBuffer();
說明:增加sb這個變量,如果需要對齊,則給a、b、c都要增加幾個空格,在變量比較多的情況下,是一種累贅的事情。
9. 【強制】IDE的text file encoding設(shè)置為UTF-8; IDE中文件的換行符使用Unix格式,不要使用windows格式。
10.【推薦】方法體內(nèi)的執(zhí)行語句組、變量的定義語句組、不同的業(yè)務(wù)邏輯之間或者不同的語義之間插入一個空行。相同業(yè)務(wù)邏輯和語義之間不需要插入空行。
說明:沒有必要插入多行空格進(jìn)行隔開。
(四) OOP規(guī)約
1. 【強制】避免通過一個類的對象引用訪問此類的靜態(tài)變量或靜態(tài)方法,無謂增加編譯器解析成本,直接用類名來訪問即可。
2. 【強制】所有的覆寫方法,必須加@Override注解。
反例:getObject()與get0bject()的問題。一個是字母的O,一個是數(shù)字的0,加@Override可以準(zhǔn)確判斷是否覆蓋成功。另外,如果在抽象類中對方法簽名進(jìn)行修改,其實現(xiàn)類會馬上編譯報錯。
3. 【強制】相同參數(shù)類型,相同業(yè)務(wù)含義,才可以使用Java的可變參數(shù),避免使用Object。
說明:可變參數(shù)必須放置在參數(shù)列表的最后。(提倡同學(xué)們盡量不用可變參數(shù)編程)
正例:public User getUsers(Stringtype, Integer... ids);
4. 【強制】對外暴露的接口簽名,原則上不允許修改方法簽名,避免對接口調(diào)用方產(chǎn)生影響。接口過時必須加@Deprecated注解,并清晰地說明采用的新接口或者新服務(wù)是什么。
5. 【強制】不能使用過時的類或方法。
說明:java.net.URLDecoder 中的方法decode(StringencodeStr) 這個方法已經(jīng)過時,應(yīng)該使用雙參數(shù)decode(String source, Stringencode)。接口提供方既然明確是過時接口,那么有義務(wù)同時提供新的接口;作為調(diào)用方來說,有義務(wù)去考證過時方法的新實現(xiàn)是什么。
6. 【強制】Object的equals方法容易拋空指針異常,應(yīng)使用常量或確定有值的對象來調(diào)用equals。
正例: "test".equals(object);
反例: object.equals("test");
說明:推薦使用java.util.Objects#equals (JDK7引入的工具類)
7. 【強制】所有的相同類型的包裝類對象之間值的比較,全部使用equals方法比較。
說明:對于Integer var=?在-128至127之間的賦值,Integer對象是在IntegerCache.cache 產(chǎn)生,會復(fù)用已有對象,這個區(qū)間內(nèi)的Integer值可以直接使用==進(jìn)行判斷,但是這個區(qū)間之外的所有數(shù)據(jù),都會在堆上產(chǎn)生,并不會復(fù)用已有對象,這是一個大坑,推薦使用equals方法進(jìn)行判斷。
8. 【強制】關(guān)于基本數(shù)據(jù)類型與包裝數(shù)據(jù)類型的使用標(biāo)準(zhǔn)如下:
1) 所有的POJO類屬性必須使用包裝數(shù)據(jù)類型。
2) RPC方法的返回值和參數(shù)必須使用包裝數(shù)據(jù)類型。
3) 所有的局部變量推薦使用基本數(shù)據(jù)類型。
說明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進(jìn)行賦值,任何
NPE問題,或者入庫檢查,都由使用者來保證。
正例:數(shù)據(jù)庫的查詢結(jié)果可能是null,因為自動拆箱,用基本數(shù)據(jù)類型接收有NPE風(fēng)險。 反例:某業(yè)務(wù)的交易報表上顯示成交總額漲跌情況,即正負(fù)x%,x為基本數(shù)據(jù)類型,調(diào)用的 RPC服務(wù),調(diào)用不成功時,返回的是默認(rèn)值,頁面顯示:0%,這是不合理的,應(yīng)該顯示成中劃線-。所以包裝數(shù)據(jù)類型的null值,能夠表示額外的信息,如:遠(yuǎn)程調(diào)用失敗,異常退出。
9. 【強制】定義DO/DTO/VO等POJO類時,不要設(shè)定任何屬性默認(rèn)值。
反例:某業(yè)務(wù)的DO的gmtCreate默認(rèn)值為newDate();但是這個屬性在數(shù)據(jù)提取時并沒有置入具體值,在更新其它字段時又附帶更新了此字段,導(dǎo)致創(chuàng)建時間被修改成當(dāng)前時間。
10.【強制】序列化類新增屬性時,請不要修改serialVersionUID字段,避免反序列失??;如果完全不兼容升級,避免反序列化混亂,那么請修改serialVersionUID值。
說明:注意serialVersionUID不一致會拋出序列化運行時異常。
11.【強制】構(gòu)造方法里面禁止加入任何業(yè)務(wù)邏輯,如果有初始化邏輯,請放在init方法中。
12.【強制】POJO類必須寫toString方法。使用工具類source> generate toString時,如果繼承了另一個POJO類,注意在前面加一下super.toString。
說明:在方法執(zhí)行拋出異常時,可以直接調(diào)用POJO的toString()方法打印其屬性值,便于排查問題。
13. 【推薦】使用索引訪問用String的split方法得到的數(shù)組時,需做最后一個分隔符后有無內(nèi)容的檢查,否則會有拋IndexOutOfBoundsException的風(fēng)險。
說明:
String str = "a,b,c,,"; String[] ary =str.split(",");
//預(yù)期大于3,結(jié)果是3
System.out.println(ary.length);
14.【推薦】當(dāng)一個類有多個構(gòu)造方法,或者多個同名方法,這些方法應(yīng)該按順序放置在一起,便于閱讀。
15.【推薦】 類內(nèi)方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter/setter方法。
說明:公有方法是類的調(diào)用者和維護者最關(guān)心的方法,首屏展示最好;保護方法雖然只是子類關(guān)心,也可能是“模板設(shè)計模式”下的核心方法;而私有方法外部一般不需要特別關(guān)心,是一個黑盒實現(xiàn);因為方法信息價值較低,所有Service和DAO的getter/setter方法放在類體最后。
16. 【推薦】setter方法中,參數(shù)名稱與類成員變量名稱一致,this.成員名=參數(shù)名。在 getter/setter方法中,盡量不要增加業(yè)務(wù)邏輯,增加排查問題難度。
反例:
public IntegergetData(){ if(true) { return data +100;
} else { return data- 100;
} }
17. 【推薦】循環(huán)體內(nèi),字符串的聯(lián)接方式,使用StringBuilder的append方法進(jìn)行擴展。
反例:
String str ="start"; for(int i=0; i<100;i++){ str = str +"hello";
}
說明:反編譯出的字節(jié)碼文件顯示每次循環(huán)都會new出一個StringBuilder對象,然后進(jìn)行 append操作,最后通過toString方法返回String對象,造成內(nèi)存資源浪費。
18.【推薦】final可提高程序響應(yīng)效率,聲明成final的情況: 1) 不需要重新賦值的變量,包括類屬性、局部變量。
2) 對象參數(shù)前加final,表示不允許修改引用的指向。
3) 類方法確定不允許被重寫。
19.【推薦】慎用Object的clone方法來拷貝對象。
說明:對象的clone方法默認(rèn)是淺拷貝,若想實現(xiàn)深拷貝需要重寫clone方法實現(xiàn)屬性對象的拷貝。
20.【推薦】類成員與方法訪問控制從嚴(yán):
1) 如果不允許外部直接通過new來創(chuàng)建對象,那么構(gòu)造方法必須是private。
2) 工具類不允許有public或default構(gòu)造方法。
3) 類非static成員變量并且與子類共享,必須是protected。
4) 類非static成員變量并且僅在本類使用,必須是private。
5) 類static成員變量如果僅在本類使用,必須是private。
6) 若是static成員變量,必須考慮是否為final。
7) 類成員方法只供類內(nèi)部調(diào)用,必須是private。
8) 類成員方法只對繼承類公開,那么限制為protected。
說明:任何類、方法、參數(shù)、變量,嚴(yán)控訪問范圍。過寬泛的訪問范圍,不利于模塊解耦。思考:如果是一個private的方法,想刪除就刪除,可是一個public的Service方法,或者一個public的成員變量,刪除一下,不得手心冒點汗嗎?變量像自己的小孩,盡量在自己的視線內(nèi),變量作用域太大,如果無限制的到處跑,那么你會擔(dān)心的。
(五) 集合處理
1. 【強制】Map/Set的key為自定義對象時,必須重寫hashCode和equals。
正例:String重寫了hashCode和equals方法,所以我們可以非常愉快地使用String對象作為key來使用。
2. 【強制】ArrayList的subList結(jié)果不可強轉(zhuǎn)成ArrayList,否則會拋出ClassCastException 異常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList; 說明:subList 返回的是 ArrayList 的內(nèi)部類 SubList,并不是 ArrayList ,而是 ArrayList 的一個視圖,對于SubList子列表的所有操作最終會反映到原列表上。
3. 【強制】在subList場景中,高度注意對原集合元素個數(shù)的修改,會導(dǎo)致子列表的遍歷、增加、刪除均產(chǎn)生ConcurrentModificationException 異常。
4. 【強制】使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[] array),傳入的是類型完全一樣的數(shù)組,大小就是list.size()。
反例:直接使用toArray無參方法存在問題,此方法返回值只能是Object[]類,若強轉(zhuǎn)其它類型數(shù)組將出現(xiàn)ClassCastException錯誤。正例:
List<String> list = newArrayList<String>(2); list.add("guan"); list.add("bao");
String[] array = newString[list.size()]; array =list.toArray(array);
說明:使用toArray帶參方法,入?yún)⒎峙涞臄?shù)組空間不夠大時,toArray方法內(nèi)部將重新分配內(nèi)存空間,并返回新數(shù)組地址;如果數(shù)組元素大于實際所需,下標(biāo)為[ list.size() ]的數(shù)組元素將被置為null,其它數(shù)組元素保持原值,因此最好將方法入?yún)?shù)組大小定義與集合元素個數(shù)一致。
5. 【強制】使用工具類Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時,不能使用其修改集合相關(guān)的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。
說明:asList的返回對象是一個Arrays內(nèi)部類,并沒有實現(xiàn)集合的修改方法。Arrays.asList 體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺的數(shù)據(jù)仍是數(shù)組。
String[] str = new String[] {"a", "b" };
List list = Arrays.asList(str);
第一種情況:list.add("c");運行時異常。第二種情況:str[0]= "gujin"; 那么list.get(0)也會隨之修改。
6. 【強制】泛型通配符<? extends T>來接收返回的數(shù)據(jù),此寫法的泛型集合不能使用add方法。說明:蘋果裝箱后返回一個<? extends Fruits>對象,此對象就不能往里加任何水果,包括蘋果。
7. 【強制】不要在foreach循環(huán)里進(jìn)行元素的remove/add操作。remove元素請使用Iterator 方式,如果并發(fā)操作,需要對Iterator對象加鎖。
反例:
List<String> a = newArrayList<String>();
a.add("1");
a.add("2"); for(String temp : a) { if("1".equals(temp)){
a.remove(temp);
}
}
說明:這個例子的執(zhí)行結(jié)果會出乎大家的意料,那么試一下把“1”換成“2”,會是同樣的結(jié)果嗎?正例:
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的比較結(jié)果和y,x的比較結(jié)果相反。
2) 傳遞性:x>y,y>z,則x>z。
3) 對稱性:x=y,則x,z比較結(jié)果和y,z比較結(jié)果相同。反例:下例中沒有處理相等的情況,實際使用中可能會出現(xiàn)異常:
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方式進(jìn)行遍歷。
說明:keySet其實是遍歷了2次,一次是轉(zhuǎn)為Iterator對象,另一次是從hashMap中取出key 所對應(yīng)的value。而entrySet只是遍歷了一次就把key和value都放到了entry中,效率更高。如果是JDK8,使用Map.foreach方法。
正例:values()返回的是V值集合,是一個list集合對象;keySet()返回的是K值集合,是一個Set集合對象;entrySet()返回的是K-V值組合集合。
11.【推薦】高度注意Map類集合K/V能不能存儲null值的情況,如下表格:
12.【參考】合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負(fù)面影響。
說明:穩(wěn)定性指集合每次遍歷的元素次序是一定的。有序性是指遍歷的結(jié)果是按某種比較規(guī)則依次排列的。如:ArrayList是order/unsort;HashMap是unorder/unsort;TreeSet是 order/sort。
13.【參考】利用Set元素唯一的特性,可以快速對另一個集合進(jìn)行去重操作,避免使用List的 contains方法進(jìn)行遍歷去重操作。
(六) 并發(fā)處理
1. 【強制】獲取單例對象要線程安全。在單例對象里面做操作也要保證線程安全。
說明:資源驅(qū)動類、工具類、單例工廠類都需要注意。
2. 【強制】線程資源必須通過線程池提供,不允許在應(yīng)用中自行顯式創(chuàng)建線程。
說明:使用線程池的好處是減少在創(chuàng)建和銷毀線程上所花的時間以及系統(tǒng)資源的開銷,解決資源不足的問題。如果不使用線程池,有可能造成系統(tǒng)創(chuàng)建大量同類線程而導(dǎo)致消耗完內(nèi)存或者
“過度切換”的問題。
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的應(yīng)用,可以使用instant代替Date,Localdatetime代替Calendar,
Datetimeformatter代替Simpledateformatter,官方給出的解釋:simple beautifulstrong immutable thread-safe。
4. 【強制】高并發(fā)時,同步調(diào)用應(yīng)該去考量鎖的性能損耗。能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖;能鎖區(qū)塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖。
5. 【強制】對多個資源、數(shù)據(jù)庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。
說明:線程一需要對表A、B、C依次全部加鎖后才可以進(jìn)行更新操作,那么線程二的加鎖順序也必須是A、B、C,否則可能出現(xiàn)死鎖。
6. 【強制】并發(fā)修改同一記錄時,避免更新丟失,要么在應(yīng)用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫層使用樂觀鎖,使用version作為更新依據(jù)。說明:如果每次訪問沖突概率小于20%,推薦使用樂觀鎖,否則使用悲觀鎖。樂觀鎖的重試次數(shù)不得小于3次。
7. 【強制】多線程并行處理定時任務(wù)時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。
8. 【強制】線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學(xué)更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風(fēng)險。
說明:Executors各個方法的弊端:
1) newFixedThreadPool和newSingleThreadExecutor: 主要問題是堆積的請求處理隊列可能會耗費非常大的內(nèi)存,甚至OOM。
2) newCachedThreadPool和newScheduledThreadPool: 主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會創(chuàng)建數(shù)量非常多的線程,甚至OOM。
9. 【強制】創(chuàng)建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。
正例:
public class TimerTaskThread extends Thread{ publicTimerTaskThread(){ super.setName("TimerTaskThread"); …
}
10.【推薦】使用CountDownLatch進(jìn)行異步轉(zhuǎn)同步操作,每個線程退出前必須調(diào)用countDown方法,線程執(zhí)行代碼注意catch異常,確保countDown方法可以執(zhí)行,避免主線程無法執(zhí)行至 countDown方法,直到超時才返回結(jié)果。說明:注意,子線程拋出異常堆棧,不能在主線程try-catch到。
11.【推薦】避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導(dǎo)致的性能下降。說明:Random實例包括java.util.Random 的實例或者 Math.random()實例。
正例:在JDK7之后,可以直接使用API ThreadLocalRandom,在 JDK7之前,可以做到每個線程一個實例。
12. 【推薦】通過雙重檢查鎖(double-checked locking)(在并發(fā)場景)實現(xiàn)延遲初始化的優(yōu)化問題隱患(可參考 The "Double-Checked Locking is Broken" Declaration),推薦問題解決方
案中較為簡單一種(適用于jdk5及以上版本),將目標(biāo)屬性聲明為 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解決多線程內(nèi)存不可見問題。對于一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。如果想取回count++數(shù)據(jù),使用如下類實現(xiàn):
AtomicIntegercount = new AtomicInteger(); count.addAndGet(1); count++操作如果是
JDK8,推薦使用LongAdder對象,比AtomicLong性能更好(減少樂觀鎖的重試次數(shù))。
14.【參考】注意HashMap的擴容死鏈,導(dǎo)致CPU飆升的問題。
15.【參考】ThreadLocal無法解決共享對象的更新問題,ThreadLocal對象建議使用static修飾。這個變量是針對一個線程內(nèi)所有操作共有的,所以設(shè)置為靜態(tài)變量,所有此類實例共享此靜態(tài)變量 ,也就是說在類第一次被使用時裝載,只分配一塊存儲空間,所有此類的對象(只要是這個線程內(nèi)定義的)都可以操控這個變量。
(七) 控制語句
1. 【強制】在一個switch塊內(nèi),每個case要么通過break/return來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個case為止;在一個switch塊內(nèi),都必須包含一個default語句并且放在最后,即使它什么代碼也沒有。
2. 【強制】在if/else/for/while/do語句中必須使用大括號,即使只有一行代碼,避免使用下面的形式:if (condition) statements;
3. 【推薦】推薦盡量少用else, if-else的方式可以改寫成:
if(condition){ … return obj; }
// 接著寫else的業(yè)務(wù)邏輯代碼;
說明:如果使用要if-else if-else方式表達(dá)邏輯,【強制】請勿超過3層,超過請使用狀態(tài)設(shè)計模式。
4. 【推薦】除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行復(fù)雜的語句,以提高可讀性。正例:
//偽代碼如下
InputStream stream = file.open(fileName,"w");
if (stream != null) {
…
} 反例:
if (file.open(fileName, "w") != null)){
…
}
5. 【推薦】循環(huán)體中的語句要考量性能,以下操作盡量移至循環(huán)體外處理,如定義對象、變量、
獲取數(shù)據(jù)庫連接,進(jìn)行不必要的try-catch操作(這個try-catch是否可以移至循環(huán)體外)。
6. 【推薦】接口入?yún)⒈Wo,這種場景常見的是用于做批量操作的接口。
7. 【參考】方法中需要進(jìn)行參數(shù)校驗的場景:
1) 調(diào)用頻次低的方法。
2) 執(zhí)行時間開銷很大的方法,參數(shù)校驗時間幾乎可以忽略不計,但如果因為參數(shù)錯誤導(dǎo)致中間執(zhí)行回退,或者錯誤,那得不償失。 3) 需要極高穩(wěn)定性和可用性的方法。
4) 對外提供的開放接口,不管是RPC/API/HTTP接口。
8. 【參考】方法中不需要參數(shù)校驗的場景:
1) 極有可能被循環(huán)調(diào)用的方法,不建議對參數(shù)進(jìn)行校驗。但在方法說明里必須注明外部參數(shù)檢查。
2) 底層的方法調(diào)用頻度都比較高,一般不校驗。畢竟是像純凈水過濾的最后一道,參數(shù)錯誤不太可能到底層才會暴露問題。一般DAO層與Service層都在同一個應(yīng)用中,部署在同一臺服務(wù)器中,所以DAO的參數(shù)校驗,可以省略。
3) 被聲明成private只會被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過檢查或者肯定不會有問題,此時可以不校驗參數(shù)。
(八) 注釋規(guī)約
1. 【強制】類、類屬性、類方法的注釋必須使用javadoc規(guī)范,使用/**內(nèi)容*/格式,不得使用
//xxx方式。
說明:在IDE編輯窗口中,javadoc方式會提示相關(guān)注釋,生成javadoc可以正確輸出相應(yīng)注釋;在IDE中,工程調(diào)用方法時,不進(jìn)入方法即可懸浮提示方法、參數(shù)、返回值的意義,提高閱讀效率。
2. 【強制】所有的抽象方法(包括接口中的方法)必須要用javadoc注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實現(xiàn)什么功能。
說明:如有實現(xiàn)和調(diào)用注意事項,請一并說明。
3. 【強制】所有的類都必須添加創(chuàng)建者信息。
4. 【強制】方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/* */注釋,注意與代碼對齊。
5. 【強制】所有的枚舉類型字段必須要有注釋,說明每個數(shù)據(jù)項的用途。
6. 【推薦】與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。專有名詞、關(guān)鍵字,保持英文原文即可。
反例:“TCP連接超時”解釋成“傳輸控制協(xié)議連接超時”,理解反而費腦筋。
7. 【推薦】代碼修改的同時,注釋也要進(jìn)行相應(yīng)的修改,尤其是參數(shù)、返回值、異常、核心邏輯等的修改。
說明:代碼與注釋更新不同步,就像路網(wǎng)與導(dǎo)航軟件更新不同步一樣,如果導(dǎo)航軟件嚴(yán)重滯后,就失去了導(dǎo)航的意義。
8. 【參考】注釋掉的代碼盡量要配合說明,而不是簡單的注釋掉。
說明:代碼被注釋掉有兩種可能性:1)后續(xù)會恢復(fù)此段代碼邏輯。2)永久不用。前者如果沒有備注信息,難以知曉注釋動機。后者建議直接刪掉(代碼倉庫保存了歷史代碼)。
9. 【參考】對于注釋的要求:第一、能夠準(zhǔn)確反應(yīng)設(shè)計思想和代碼邏輯;第二、能夠描述業(yè)務(wù)含義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒有注釋的大段代碼對于閱讀者形同天書,注釋是給自己看的,即使隔很長時間,也能清晰理解當(dāng)時的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作。
10.【參考】好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡準(zhǔn)確、表達(dá)到位。避免出現(xiàn)注釋的一個極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當(dāng)大的負(fù)擔(dān)。
反例:
// put elephant into fridge put(elephant,fridge);
方法名put,加上兩個有意義的變量名elephant和fridge,已經(jīng)說明了這是在干什么,語義清晰的代碼不需要額外的注釋。
11.【參考】特殊注釋標(biāo)記,請注明標(biāo)記人與標(biāo)記時間。注意及時處理這些標(biāo)記,通過標(biāo)記掃描,經(jīng)常清理此類標(biāo)記。線上故障有時候就是來源于這些標(biāo)記處的代碼。 1) 待辦事宜(TODO):( 標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])
表示需要實現(xiàn),但目前還未實現(xiàn)的功能。這實際上是一個javadoc的標(biāo)簽,目前的
javadoc還沒有實現(xiàn),但已經(jīng)被廣泛使用。只能應(yīng)用于類,接口和方法(因為它是一個javadoc標(biāo)簽)。
2)錯誤,不能工作(FIXME):(標(biāo)記人,標(biāo)記時間,[預(yù)計處理時間])
在注釋中用FIXME標(biāo)記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。
(九) 其它
1. 【強制】在使用正則表達(dá)式時,利用好其預(yù)編譯功能,可以有效加快正則匹配速度。
說明:不要在方法體內(nèi)定義:Pattern pattern = Pattern.compile(規(guī)則);
2. 【強制】避免用Apache Beanutils進(jìn)行屬性的copy。
說明:Apache BeanUtils性能較差,可以使用其他方案比如SpringBeanUtils, Cglib
BeanCopier。
3. 【強制】velocity調(diào)用POJO類的屬性時,建議直接使用屬性名取值即可,模板引擎會自動按規(guī)范調(diào)用POJO的getXxx(),如果是boolean基本數(shù)據(jù)類型變量(注意,boolean命名不需要加is前綴),會自動調(diào)用isXxx()方法。
說明:注意如果是Boolean包裝類對象,優(yōu)先調(diào)用getXxx()的方法。
4. 【強制】后臺輸送給頁面的變量必須加$!{var}——中間的感嘆號。
說明:如果var=null或者不存在,那么${var}會直接顯示在頁面上。
5. 【強制】注意 Math.random() 這個方法返回是double類型,注意取值范圍 0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數(shù)類型的隨機數(shù),不要將x放大10的若干倍然后取整,直接使用Random對象的nextInt或者nextLong方法。
6. 【強制】獲取當(dāng)前毫秒數(shù):System.currentTimeMillis(); 而不是new Date().getTime(); 說明:如果想獲取更加精確的納秒級時間值,用System.nanoTime。在JDK8中,針對統(tǒng)計時間等場景,推薦使用Instant類。
7. 【推薦】盡量不要在vm中加入變量聲明、邏輯運算符,更不要在vm模板中加入任何復(fù)雜的邏輯。
8. 【推薦】任何數(shù)據(jù)結(jié)構(gòu)的使用都應(yīng)限制大小。
說明:這點很難完全做到,但很多次的故障都是因為數(shù)據(jù)結(jié)構(gòu)自增長,結(jié)果造成內(nèi)存被吃光。
9. 【推薦】對于“明確停止使用的代碼和配置”,如方法、變量、類、配置文件、動態(tài)配置屬性等要堅決從程序中清理出去,避免造成過多垃圾。清理這類垃圾代碼是技術(shù)氣場,不要有這樣的觀念:“不做不錯,多做多錯”。
二、異常日志
(一) 異常處理
1. 【強制】不要捕獲Java類庫中定義的繼承自RuntimeException的運行時異常類,如:
IndexOutOfBoundsException/ NullPointerException,這類異常由程序員預(yù)檢查來規(guī)避,保證程序健壯性。
正例:if(obj != null) {...}
反例:try { obj.method() }catch(NullPointerException e){…}
2. 【強制】異常不要用來做流程控制,條件控制,因為異常的處理效率比條件分支低。
3. 【強制】對大段代碼進(jìn)行try-catch,這是不負(fù)責(zé)任的表現(xiàn)。catch時請分清穩(wěn)定代碼和非穩(wěn)定代碼,穩(wěn)定代碼指的是無論如何不會出錯的代碼。對于非穩(wěn)定代碼的catch盡可能進(jìn)行區(qū)分異常類型,再做對應(yīng)的異常處理。
4. 【強制】捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調(diào)用者。最外層的業(yè)務(wù)使用者,必須處理異常,將其轉(zhuǎn)化為用戶可以理解的內(nèi)容。
5. 【強制】有try塊放到了事務(wù)代碼中,catch異常后,如果需要回滾事務(wù),一定要注意手動回滾事務(wù)。
6. 【強制】finally塊必須對資源對象、流對象進(jìn)行關(guān)閉,有異常也要做try-catch。
說明:如果JDK7,可以使用try-with-resources方法。
7. 【強制】不能在finally塊中使用return,finally塊中的return返回后方法結(jié)束執(zhí)行,不會再執(zhí)行try塊中的return語句。
8. 【強制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。
說明:如果預(yù)期拋的是繡球,實際接到的是鉛球,就會產(chǎn)生意外情況。
9. 【推薦】方法的返回值可以為null,不強制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回null值。調(diào)用方需要進(jìn)行null判斷防止NPE問題。
說明:本規(guī)約明確防止NPE是調(diào)用者的責(zé)任。即使被調(diào)用方法返回空集合或者空對象,對調(diào)用
者來說,也并非高枕無憂,必須考慮到遠(yuǎn)程調(diào)用失敗,運行時異常等場景返回null的情況。
10.【推薦】防止NPE,是程序員的基本修養(yǎng),注意NPE產(chǎn)生的場景:
1) 返回類型為包裝數(shù)據(jù)類型,有可能是null,返回int值時注意判空。
反例:public int f(){return Integer對象},如果為null,自動解箱拋NPE。
2) 數(shù)據(jù)庫的查詢結(jié)果可能為null。
3) 集合里的元素即使isNotEmpty,取出的數(shù)據(jù)元素也可能為null。
4) 遠(yuǎn)程調(diào)用返回對象,一律要求進(jìn)行NPE判斷。
5) 對于Session中獲取的數(shù)據(jù),建議NPE檢查,避免空指針。
6) 級聯(lián)調(diào)用obj.getA().getB().getC();一連串調(diào)用,易產(chǎn)生NPE。
11.【推薦】在代碼中使用“拋異?!边€是“返回錯誤碼”,對于公司外的http/api開放接口必須使用“錯誤碼”;而應(yīng)用內(nèi)部推薦異常拋出;跨應(yīng)用間RPC調(diào)用優(yōu)先考慮使用Result方式,封裝isSuccess、“錯誤碼”、“錯誤簡短信息”。
說明:關(guān)于RPC方法返回方式使用Result方式的理由:
1) 使用拋異常返回方式,調(diào)用方如果沒有捕獲到就會產(chǎn)生運行時錯誤。
2) 如果不加棧信息,只是new自定義異常,加入自己的理解的error message,對于調(diào)用端解決問題的幫助不會太多。如果加了棧信息,在頻繁調(diào)用出錯的情況下,數(shù)據(jù)序列化和傳輸?shù)男阅軗p耗也是問題。
12.【推薦】定義時區(qū)分unchecked / checked 異常,避免直接使用RuntimeException拋出,更不允許拋出Exception或者Throwable,應(yīng)使用有業(yè)務(wù)含義的自定義異常。推薦業(yè)界已定義過的自定義異常,如:DaoException / ServiceException等。
13.【參考】避免出現(xiàn)重復(fù)的代碼(Don'tRepeat Yourself),即DRY原則。
說明:隨意復(fù)制和粘貼代碼,必然會導(dǎo)致代碼的重復(fù),在以后需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是共用模塊。
正例:一個類中有多個public方法,都需要進(jìn)行數(shù)行相同的參數(shù)校驗操作,這個時候請抽取:
private boolean checkParam(DTO dto){...}
(二) 日志規(guī)約
1. 【強制】應(yīng)用中不可直接使用日志系統(tǒng)(Log4j、Logback)中的API,而應(yīng)依賴使用日志框架
SLF4J中的API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統(tǒng)一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger =LoggerFactory.getLogger(Abc.class);
2. 【強制】日志文件推薦至少保存15天,因為有些異常具備以“周”為頻次發(fā)生的特點。
3. 【強制】應(yīng)用中的擴展日志(如打點、臨時監(jiān)控、訪問日志等)命名方式:
appName_logType_logName.log。logType:日志類型,推薦分類有stats/desc/monitor/visit 等;logName:日志描述。這種命名的好處:通過文件名就可知道日志文件屬于什么應(yīng)用,什么
類型,什么目的,也有利于歸類查找。
正例:mppserver應(yīng)用中單獨監(jiān)控時區(qū)轉(zhuǎn)換異常,如: mppserver_monitor_timeZoneConvert.log
說明:推薦對日志進(jìn)行分類,錯誤日志和業(yè)務(wù)日志盡量分開存放,便于開發(fā)人員查看,也便于通過日志對系統(tǒng)進(jìn)行及時監(jiān)控。
4. 【強制】對trace/debug/info級別的日志輸出,必須使用條件輸出形式或者使用占位符的方式。
說明:logger.debug("Processingtrade with id: " + id + " symbol: " + symbol); 如果日志級別是warn,上述日志不會打印,但是會執(zhí)行字符串拼接操作,如果symbol是對象,會執(zhí)行toString()方法,浪費了系統(tǒng)資源,執(zhí)行了上述操作,最終日志卻沒有打印。
正例:(條件)
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " +id + " symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} andsymbol : {} ", id, symbol);
5. 【強制】避免重復(fù)打印日志,浪費磁盤空間,務(wù)必在log4j.xml中設(shè)置additivity=false。
正例:<loggername="com.taobao.ecrm.member.config" additivity="false">
6. 【強制】異常信息應(yīng)該包括兩類信息:案發(fā)現(xiàn)場信息和異常堆棧信息。如果不處理,那么往上拋。
正例:logger.error(各類參數(shù)或者對象toString +"_" + e.getMessage(), e);
7. 輸出的POJO類必須重寫toString方法,否則只輸出此對象的hashCode值(地址值),沒啥參考意義。
8. 【推薦】可以使用warn日志級別來記錄用戶輸入?yún)?shù)錯誤的情況,避免用戶投訴時,無所適從。注意日志輸出的級別,error級別只記錄系統(tǒng)邏輯出錯、異常、或者重要的錯誤信息。如非必要,請不要在此場景打出error級別,避免頻繁報警。
9. 【推薦】謹(jǐn)慎地記錄日志。生產(chǎn)環(huán)境禁止輸出debug日志;有選擇地輸出info日志;如果使用warn來記錄剛上線時的業(yè)務(wù)行為信息,一定要注意日志輸出量的問題,避免把服務(wù)器磁盤撐爆,并記得及時刪除這些觀察日志。
說明:大量地輸出無效日志,不利于系統(tǒng)性能提升,也不利于快速定位錯誤點。紀(jì)錄日志時請思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?
10.【參考】如果日志用英文描述不清楚,推薦使用中文注釋。對于中文UTF-8的日志,在secureCRT 中,set encoding=utf-8;如果中文字符還亂碼,請設(shè)置:全局>默認(rèn)的會話設(shè)置>外觀>字體> 選擇字符集gb2312;如果還不行,執(zhí)行命令:set termencoding=gbk,并且直接使用中文來進(jìn)行檢索。
三、MYSQL規(guī)約
(一) 建表規(guī)約
1. 【強制】表達(dá)是與否概念的字段,必須使用is_xxx的方式命名,數(shù)據(jù)類型是unsigned tinyint
( 1表示是,0表示否),此規(guī)則同樣適用于odps建表。
說明:任何字段如果為非負(fù)數(shù),必須是unsigned。
2. 【強制】表名、字段名必須使用小寫字母或數(shù)字;禁止出現(xiàn)數(shù)字開頭,禁止兩個下劃線中間只出現(xiàn)數(shù)字。數(shù)據(jù)庫字段名的修改代價很大,因為無法進(jìn)行預(yù)發(fā)布,所以字段名稱需要慎重考慮。
正例:getter_admin,task_config,level3_name 反例:GetterAdmin,taskConfig,level_3_name
3. 【強制】表名不使用復(fù)數(shù)名詞。
說明:表名應(yīng)該僅僅表示表里面的實體內(nèi)容,不應(yīng)該表示實體數(shù)量,對應(yīng)于DO類名也是單數(shù)形式,符合表達(dá)習(xí)慣。
4. 【強制】禁用保留字,如desc、range、match、delayed等,參考官方保留字。
5. 【強制】唯一索引名為uk_字段名;普通索引名則為idx_字段名。
說明:uk_即 unique key;idx_ 即index的簡稱。
6. 【強制】小數(shù)類型為decimal,禁止使用float和double。
說明:float和double在存儲的時候,存在精度損失的問題,很可能在值的比較時,得到不
正確的結(jié)果。如果存儲的數(shù)據(jù)范圍超過decimal的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開存儲。
7. 【強制】如果存儲的字符串長度幾乎相等,使用CHAR定長字符串類型。
8. 【強制】varchar是可變長字符串,不預(yù)先分配存儲空間,長度不要超過5000,如果存儲長度大于此值,定義字段類型為TEXT,獨立出來一張表,用主鍵來對應(yīng),避免影響其它字段索引效率。
9. 【強制】表必備三字段:id, gmt_create, gmt_modified。
說明:其中id必為主鍵,類型為unsigned bigint、單表時自增、步長為1;分表時改為從
TDDL Sequence取值,確保分表之間的全局唯一。gmt_create,gmt_modified的類型均為 date_time類型。
10.【推薦】表的命名最好是加上“業(yè)務(wù)名稱_表的作用”,避免上云梯后,再與其它業(yè)務(wù)表關(guān)聯(lián)時有混淆。
正例:tiger_task/ tiger_reader / mpp_config
11.【推薦】庫名與應(yīng)用名稱盡量一致。
12.【推薦】如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時,需要及時更新字段注釋。
13.【推薦】字段允許適當(dāng)冗余,以提高性能,但是必須考慮數(shù)據(jù)同步的情況。冗余字段應(yīng)遵循:
1) 不是頻繁修改的字段。
2) 不是varchar超長字段,更不能是text字段。
正例:各業(yè)務(wù)線經(jīng)常冗余存儲商品名稱,避免查詢時需要調(diào)用IC服務(wù)獲取。
14.【推薦】單表行數(shù)超過500萬行或者單表容量超過2GB,才推薦進(jìn)行分庫分表。
說明:如果預(yù)計三年后的數(shù)據(jù)量根本達(dá)不到這個級別,請不要在創(chuàng)建表時就分庫分表。
反例:某業(yè)務(wù)三年總數(shù)據(jù)量才2萬行,卻分成1024張表,問:你為什么這么設(shè)計?答:分1024 張表,不是標(biāo)配嗎?
15.【參考】合適的字符存儲長度,不但節(jié)約數(shù)據(jù)庫表空間、節(jié)約索引存儲,更重要的是提升檢索速度。
正例:人的年齡用unsigned tinyint(表示范圍0-255,人的壽命不會超過255歲);海龜就必須是smallint,但如果是太陽的年齡,就必須是int;如果是所有恒星的年齡都加起來,那么就必須使用bigint。
(二) 索引規(guī)約
1. 【強制】業(yè)務(wù)上具有唯一特性的字段,即使是組合字段,也必須建成唯一索引。
說明:不要以為唯一索引影響了insert速度,這個速度損耗可以忽略,但提高查找速度是明顯的;另外,即使在應(yīng)用層做了非常完善的校驗和控制,只要沒有唯一索引,根據(jù)墨菲定律,必然有臟數(shù)據(jù)產(chǎn)生。
2. 【強制】超過三個表禁止join。需要join的字段,數(shù)據(jù)類型保持絕對一致;多表關(guān)聯(lián)查詢時,保證被關(guān)聯(lián)的字段需要有索引。
說明:即使雙表join也要注意表索引、SQL性能。
3. 【強制】在varchar字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據(jù)實際文本區(qū)分度決定索引長度。
說明:索引的長度與區(qū)分度是一對矛盾體,一般對字符串類型數(shù)據(jù),長度為20的索引,區(qū)分度會高達(dá)90%以上,可以使用count(distinct left(列名, 索引長度))/count(*)的區(qū)分度來確定。
4. 【強制】頁面搜索嚴(yán)禁左模糊或者全模糊,如果需要請走搜索引擎來解決。
說明:索引文件具有B-Tree的最左前綴匹配特性,如果左邊的值未確定,那么無法使用此索引。
5. 【推薦】如果有order by的場景,請注意利用索引的有序性。order by 最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort的情況,影響查詢性能。
正例:where a=? and b=? order by c; 索引:a_b_c
反例:索引中有范圍查找,那么索引有序性無法利用,如:WHERE a>10 ORDER BY b; 索引a_b 無法排序。
6. 【推薦】利用覆蓋索引來進(jìn)行查詢操作,來避免回表操作。
說明:如果一本書需要知道第11章是什么標(biāo)題,會翻開第11章對應(yīng)的那一頁嗎?目錄瀏覽一下就好,這個目錄就是起到覆蓋索引的作用。
正例:IDB能夠建立索引的種類:主鍵索引、唯一索引、普通索引,而覆蓋索引是一種查詢的一種效果,用explain的結(jié)果,extra列會出現(xiàn):using index.
7. 【推薦】利用延遲關(guān)聯(lián)或者子查詢優(yōu)化超多分頁場景。
說明:MySQL并不是跳過offset行,而是取offset+N行,然后返回放棄前offset行,返回N 行,那當(dāng)offset特別大的時候,效率就非常的低下,要么控制返回的總頁數(shù),要么對超過特定閾值的頁數(shù)進(jìn)行SQL改寫。
正例:先快速定位需要獲取的id段,然后再關(guān)聯(lián):
SELECT a.* FROM 表1 a, (select id from表1 where 條件 LIMIT 100000,20 ) bwhere a.id=b.id
8. 【推薦】 SQL性能優(yōu)化的目標(biāo):至少要達(dá)到range 級別,要求是ref級別,如果可以是consts 最好。說明:
1) consts 單表中最多只有一個匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
2) ref 指的是使用普通的索引。(normalindex)
3) range 對索引進(jìn)范圍檢索。
反例:explain表的結(jié)果,type=index,索引物理文件全掃描,速度非常慢,這個index級別比較range還低,與全表掃描是小巫見大巫。
9. 【推薦】建組合索引的時候,區(qū)分度最高的在最左邊。
正例:如果where a=? and b=? ,a列的幾乎接近于唯一值,那么只需要單建idx_a索引即可。說明:存在非等號和等號混合判斷條件時,在建索引時,請把等號條件的列前置。如:where a>?
and b=? 那么即使a的區(qū)分度更高,也必須把b放在索引的最前列。
10.【參考】創(chuàng)建索引時避免有如下極端誤解:
1) 誤認(rèn)為一個查詢就需要建一個索引。
2) 誤認(rèn)為索引會消耗空間、嚴(yán)重拖慢更新和新增速度。
3) 誤認(rèn)為唯一索引一律需要在應(yīng)用層通過“先查后插”方式解決。
(三) SQL規(guī)約
1. 【強制】不要使用count(列名)或count(常量)來替代count(*),count(*)就是SQL92定義的標(biāo)準(zhǔn)統(tǒng)計行數(shù)的語法,跟數(shù)據(jù)庫無關(guān),跟NULL和非NULL無關(guān)。
說明:count(*)會統(tǒng)計值為NULL的行,而count(列名)不會統(tǒng)計此列為NULL值的行。
2. 【強制】count(distinct col) 計算該列除NULL之外的不重復(fù)數(shù)量。注意 count(distinct col1, col2) 如果其中一列全為NULL,那么即使另一列有不同的值,也返回為0。
3. 【強制】當(dāng)某一列的值全是NULL時,count(col)的返回結(jié)果為0,但sum(col)的返回結(jié)果為
NULL,因此使用sum()時需注意NPE問題。
正例:可以使用如下方式來避免sum的NPE問題:SELECTIF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
4. 【強制】使用ISNULL()來判斷是否為NULL值。注意:NULL與任何值的直接比較都為NULL。
說明:
1) NULL<>NULL的返回結(jié)果是NULL,不是false。
2) NULL=NULL的返回結(jié)果是NULL,不是true。
3) NULL<>1的返回結(jié)果是NULL,而不是true。
5. 【強制】在代碼中寫分頁查詢邏輯時,若count為0應(yīng)直接返回,避免執(zhí)行后面的分頁語句。
6. 【強制】不得使用外鍵與級聯(lián),一切外鍵概念必須在應(yīng)用層解決。
說明:(概念解釋)學(xué)生表中的student_id是主鍵,那么成績表中的student_id則為外鍵。
如果更新學(xué)生表中的student_id,同時觸發(fā)成績表中的student_id更新,則為級聯(lián)更新。外鍵與級聯(lián)更新適用于單機低并發(fā),不適合分布式、高并發(fā)集群;級聯(lián)更新是強阻塞,存在數(shù)據(jù)庫更新風(fēng)暴的風(fēng)險;外鍵影響數(shù)據(jù)庫的插入速度。
7. 【強制】禁止使用存儲過程,存儲過程難以調(diào)試和擴展,更沒有移植性。
8. 【強制】IDB數(shù)據(jù)訂正時,刪除和修改記錄時,要先select,避免出現(xiàn)誤刪除,確認(rèn)無誤才能提交執(zhí)行。
9. 【推薦】in操作能避免則避免,若實在避免不了,需要仔細(xì)評估in后邊的集合元素數(shù)量,控制在1000個之內(nèi)。
10.【參考】因阿里巴巴全球化需要,所有的字符存儲與表示,均以utf-8編碼,那么字符計數(shù)方法注意:
說明:
SELECT LENGTH("阿里巴巴"); 返回為12
SELECT CHARACTER_LENGTH("阿里巴巴"); 返回為4
如果要使用表情,那么使用utfmb4來進(jìn)行存儲,注意它與utf-8編碼。
11.【參考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系統(tǒng)和事務(wù)日志資源少,但TRUNCATE 無事務(wù)且不觸發(fā)trigger,有可能造成事故,故不建議在開發(fā)代碼中使用此語句。
說明:TRUNCATETABLE 在功能上與不帶 WHERE 子句的 DELETE 語句相同。
(四) ORM規(guī)約
1. 【強制】在表查詢中,一律不要使用 * 作為查詢的字段列表,需要哪些字段必須明確寫明。
說明:1)增加查詢分析器解析成本。2)增減字段容易與resultMap配置不一致。
2. 【強制】POJO類的boolean屬性不能加is,而數(shù)據(jù)庫字段必須加is_,要求在resultMap中進(jìn)行字段與屬性之間的映射。
說明:參見定義POJO類以及數(shù)據(jù)庫字段定義規(guī)定,在sql.xml增加映射,是必須的。
3. 【強制】不要用resultClass當(dāng)返回參數(shù),即使所有類屬性名與數(shù)據(jù)庫字段一一對應(yīng),也需要定義;反過來,每一個表也必然有一個與之對應(yīng)。
說明:配置映射關(guān)系,使字段與DO類解耦,方便維護。
4. 【強制】xml配置中參數(shù)注意使用:#{},#param#不要使用${} 此種方式容易出現(xiàn)SQL注入。
5. 【強制】iBATIS自帶的queryForList(StringstatementName,int start,int size)不推薦使用。
說明:其實現(xiàn)方式是在數(shù)據(jù)庫取到statementName對應(yīng)的SQL語句的所有記錄,再通過subList 取start,size的子集合,線上因為這個原因曾經(jīng)出現(xiàn)過OOM。
正例:在sqlmap.xml中引入 #start#, #size#
Map<String, Object> map = new HashMap<String,Object>(); map.put("start",start); map.put("size", size);
6. 【強制】不允許直接拿HashMap與HashTable作為查詢結(jié)果集的輸出。
反例:某同學(xué)為避免寫一個<resultMap>,直接使用HashTable來接收數(shù)據(jù)庫返回結(jié)果,結(jié)果出現(xiàn)日常是把bigint轉(zhuǎn)成Long值,而線上由于數(shù)據(jù)庫版本不一樣,解析成BigInteger,導(dǎo)致線上問題。
7. 【強制】更新數(shù)據(jù)表記錄時,必須同時更新記錄對應(yīng)的gmt_modified字段值為當(dāng)前時間。
8. 【推薦】不要寫一個大而全的數(shù)據(jù)更新接口,傳入為POJO類,不管是不是自己的目標(biāo)更新字段,都進(jìn)行update table set c1=value1,c2=value2,c3=value3; 這是不對的。執(zhí)行SQL時,盡量不要更新無改動的字段,一是易出錯;二是效率低;三是binlog增加存儲。
9. 【參考】@Transactional事務(wù)不要濫用。事務(wù)會影響數(shù)據(jù)庫的QPS,另外使用事務(wù)的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統(tǒng)計修正等。
10.【參考】<isEqual>中的compareValue是與屬性值對比的常量,一般是數(shù)字,表示相等時帶上此條件;<isNotEmpty>表示不為空且不為null時執(zhí)行;<isNotNull>表示不為null值時執(zhí)行。
四、工程規(guī)約
(一) 應(yīng)用分層
1. 【推薦】圖中默認(rèn)上層依賴于下層,箭頭關(guān)系表示可直接依賴,如:開放接口層可以依賴于
Web層,也可以直接依賴于Service層,依此類推:
• 開放接口層:可直接封裝Service接口暴露成RPC接口;通過Web封裝成http接口;網(wǎng)關(guān)控制層等。
• 終端顯示層:各個端的模板渲染并執(zhí)行顯示層。當(dāng)前主要是velocity渲染,JS渲染,JSP渲染,移動端展示層等。
• Web層:主要是對訪問控制進(jìn)行轉(zhuǎn)發(fā),各類基本參數(shù)校驗,或者不復(fù)用的業(yè)務(wù)簡單處理等。
• Service層:相對具體的業(yè)務(wù)邏輯服務(wù)層。
• Manager層:通用業(yè)務(wù)處理層,它有如下特征:
1) 對第三方平臺封裝的層,預(yù)處理返回結(jié)果及轉(zhuǎn)化異常信息;
2) 對Service層通用能力的下沉,如緩存方案、中間件通用處理;
3) 與DAO層交互,對DAO的業(yè)務(wù)通用能力的封裝。
• DAO層:數(shù)據(jù)訪問層,與底層Mysql、Oracle、Hbase、OB進(jìn)行數(shù)據(jù)交互。
• 外部接口或第三方平臺:包括其它部門RPC開放接口,基礎(chǔ)平臺,其它公司的HTTP接口。
2. 【參考】(分層異常處理規(guī)約)在DAO層,產(chǎn)生的異常類型有很多,無法用細(xì)粒度異常進(jìn)行
catch,使用catch(Exception e)方式,并throw newDaoException(e),不需要打印日志,
因為日志在Manager/Service層一定需要捕獲并打到日志文件中去,如果同臺服務(wù)器再打日志,浪費性能和存儲。在Service層出現(xiàn)異常時,必須記錄日志信息到磁盤,盡可能帶上參數(shù)信息,相當(dāng)于保護案發(fā)現(xiàn)場。如果Manager層與Service同機部署,日志方式與DAO層處理一致,如果是單獨部署,則采用與Service一致的處理方式。Web層絕不應(yīng)該繼續(xù)往上拋異常,因為已經(jīng)處于頂層,無繼續(xù)處理異常的方式,如果意識到這個異常將導(dǎo)致頁面無法正常渲染,那么就應(yīng)該直接跳轉(zhuǎn)到友好錯誤頁面,盡量加上友好的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。
3. 【參考】分層領(lǐng)域模型規(guī)約:
• DO(Data Object):與數(shù)據(jù)庫表結(jié)構(gòu)一一對應(yīng),通過DAO層向上傳輸數(shù)據(jù)源對象。
• DTO(Data Transfer Object):數(shù)據(jù)傳輸對象,Service和Manager向外傳輸?shù)膶ο蟆?br />
• BO(Business Object):業(yè)務(wù)對象。可以由Service層輸出的封裝業(yè)務(wù)邏輯的對象。
• QUERY:數(shù)據(jù)查詢對象,各層接收上層的查詢請求。注:超過2個參數(shù)的查詢封裝,禁止使用Map類來傳輸。
• VO(View Object):顯示層對象,通常是Web向模板渲染引擎層傳輸?shù)膶ο蟆?br />
(二) 二方庫規(guī)約
1. 【強制】定義GAV遵從以下規(guī)則:
1) GroupID格式:com.{公司/BU }.業(yè)務(wù)線.[子業(yè)務(wù)線],最多4級。
說明:{公司/BU}例如:alibaba/taobao/tmall/aliexpress等BU一級;子業(yè)務(wù)線可選。
正例:com.taobao.tddl 或 com.alibaba.sourcing.multilang
2) ArtifactID格式:產(chǎn)品線名-模塊名。語義不重復(fù)不遺漏,先到倉庫中心去查證一下。
正例:tc-client / uic-api / tair-tool
3) Version:詳細(xì)規(guī)定參考下方。
2. 【強制】二方庫版本號命名方式:主版本號.次版本號.修訂號
1) 主版本號:當(dāng)做了不兼容的API 修改,或者增加了能改變產(chǎn)品方向的新功能。
2) 次版本號:當(dāng)做了向下兼容的功能性新增(新增類、接口等)。
3) 修訂號:修復(fù)bug,沒有修改方法簽名的功能加強,保持 API 兼容性。
3. 【強制】線上應(yīng)用不要依賴SNAPSHOT版本(安全包除外);正式發(fā)布的類庫必須使用RELEASE 版本號升級+1的方式,且版本號不允許覆蓋升級,必須去中央倉庫進(jìn)行查證。
說明:不依賴SNAPSHOT版本是保證應(yīng)用發(fā)布的冪等性。另外,也可以加快編譯時的打包構(gòu)建。
4. 【強制】二方庫的新增或升級,保持除功能點之外的其它jar包仲裁結(jié)果不變。如果有改變,必須明確評估和驗證,建議進(jìn)行dependency:resolve前后信息比對,如果仲裁結(jié)果完全不一致,那么通過dependency:tree命令,找出差異點,進(jìn)行<excludes>排除jar包。
5. 【強制】二方庫里可以定義枚舉類型,參數(shù)可以使用枚舉類型,但是接口返回值不允許使用枚舉類型或者包含枚舉類型的POJO對象。
6. 【強制】依賴于一個二方庫群時,必須定義一個統(tǒng)一版本變量,避免版本號不一致。
說明:依賴springframework-core,-context,-beans,它們都是同一個版本,可以定義一個變量來保存版本:${spring.version},定義依賴的時候,引用該版本。
7. 【強制】禁止在子項目的pom依賴中出現(xiàn)相同的GroupId,相同的ArtifactId,但是不同的
Version。
說明:在本地調(diào)試時會使用各子項目指定的版本號,但是合并成一個war,只能有一個版本號出現(xiàn)在最后的lib目錄中。曾經(jīng)出現(xiàn)過線下調(diào)試是正確的,發(fā)布到線上出故障的先例。
8. 【推薦】工具類二方庫已經(jīng)提供的,盡量不要在本應(yīng)用中編程實現(xiàn)。
l json操作: fastjson
l md5操作:commons-codec
l 工具集合:Guava包
l 數(shù)組操作:ArrayUtils(org.apache.commons.lang3.ArrayUtils)
l 集合操作:CollectionUtils(org.apache.commons.collections4.CollectionUtils)
l 除上面以外還有NumberUtils、DateFormatUtils、DateUtils等優(yōu)先使用 org.apache.commons.lang3這個包下的,不要使用org.apache.commons.lang包下面的。原因是commons.lang這個包是從JDK1.2開始支持的所以很多1.5/1.6的特性是不支持的,例如:泛型。
9. 【推薦】所有pom文件中的依賴聲明放在<dependencies>語句塊中,所有版本仲裁放在
<dependencyManagement>語句塊中。
說明:<dependencyManagement>里只是聲明版本,并不實現(xiàn)引入,因此子項目需要顯式的聲明
依賴,version和scope都讀取自父pom。而<dependencies>所有聲明在主pom的<dependencies > 里的依賴都會自動引入,并默認(rèn)被所有的子項目繼承。
10.【推薦】二方庫盡量不要有配置項,最低限度不要再增加配置項。
11.【參考】為避免應(yīng)用二方庫的依賴沖突問題,二方庫發(fā)布者應(yīng)當(dāng)遵循以下原則:
1) 精簡可控原則。移除一切不必要的API和依賴,只包含 Service API、必要的領(lǐng)域模型對象、Utils類、常量、枚舉等。如果依賴其它二方庫,盡量是provided引入,讓二方庫使用者去依賴具體版本號;無log具體實現(xiàn),只依賴日志框架。
2) 穩(wěn)定可追溯原則。每個版本的變化應(yīng)該被記錄,二方庫由誰維護,源碼在哪里,都需要能方便查到。除非用戶主動升級版本,否則公共二方庫的行為不應(yīng)該發(fā)生變化。
(三) 服務(wù)器規(guī)約
1. 【推薦】高并發(fā)服務(wù)器建議調(diào)小TCP協(xié)議的time_wait超時時間。
說明:操作系統(tǒng)默認(rèn)240秒后,才會關(guān)閉處于time_wait狀態(tài)的連接,在高并發(fā)訪問下,服務(wù)器端會因為處于time_wait的連接數(shù)太多,可能無法建立新的連接,所以需要在服務(wù)器上調(diào)小此等待值。
正例:在linux服務(wù)器上請通過變更/etc/sysctl.conf文件去修改該缺省值(秒): net.ipv4.tcp_fin_timeout = 30
2. 【推薦】調(diào)大服務(wù)器所支持的最大文件句柄數(shù)(File Descriptor,簡寫為fd)。
說明:主流操作系統(tǒng)的設(shè)計是將TCP/UDP連接采用與文件一樣的方式去管理,即一個連接對應(yīng)于一個fd。主流的linux服務(wù)器默認(rèn)所支持最大fd數(shù)量為1024,當(dāng)并發(fā)連接數(shù)很大時很容易因為fd不足而出現(xiàn)“open too many files”錯誤,導(dǎo)致新的連接無法建立。建議將linux 服務(wù)器所支持的最大句柄數(shù)調(diào)高數(shù)倍(與服務(wù)器的內(nèi)存數(shù)量相關(guān))。
3. 【推薦】給JVM設(shè)置-XX:+HeapDumpOnOutOfMemoryError參數(shù),讓JVM碰到OOM場景時輸出dump 信息。
說明:OOM的發(fā)生是有概率的,甚至有規(guī)律地相隔數(shù)月才出現(xiàn)一例,出現(xiàn)時的現(xiàn)場信息對查錯非常有價值。
4. 【參考】服務(wù)器內(nèi)部重定向必須使用forward;外部重定向地址必須使用URL Broker生成,否則因線上采用HTTPS協(xié)議而導(dǎo)致瀏覽器提示“不安全”。此外,還會帶來URL維護不一致的問題。
五、安全規(guī)約
1. 【強制】可被用戶直接訪問的功能必須進(jìn)行權(quán)限控制校驗。說明:防止沒有做權(quán)限控制就可隨意訪問、操作別人的數(shù)據(jù),比如查看、修改別人的訂單。
2. 【強制】用戶敏感數(shù)據(jù)禁止直接展示,必須對展示數(shù)據(jù)脫敏。說明:支付寶中查看個人手機號碼會顯示成:158****9119,隱藏中間4位,防止隱私泄露。
3. 【強制】用戶輸入的SQL參數(shù)嚴(yán)格使用參數(shù)綁定或者M(jìn)ETADATA字段值限定,防止SQL注入,禁止字符串拼接SQL訪問數(shù)據(jù)庫。
4. 【強制】用戶請求傳入的任何參數(shù)必須做有效性驗證。說明:忽略參數(shù)校驗可能導(dǎo)致:
l page size過大導(dǎo)致內(nèi)存溢出
l 惡意order by導(dǎo)致數(shù)據(jù)庫慢查詢
l 正則輸入源串拒絕服務(wù)ReDOS
l 任意重定向
l SQL注入
l Shell注入
l 反序列化注入
5. 【強制】禁止向HTML頁面輸出未經(jīng)安全過濾或未正確轉(zhuǎn)義的用戶數(shù)據(jù)。
6. 【強制】表單、AJAX提交必須執(zhí)行CSRF安全過濾。
說明:CSRF(Cross-siterequest forgery)跨站請求偽造是一類常見編程漏洞。對于存在CSRF
漏洞的應(yīng)用/網(wǎng)站,攻擊者可以事先構(gòu)造好URL,只要受害者用戶一訪問,后臺便在用戶不知情情況下對數(shù)據(jù)庫中用戶參數(shù)進(jìn)行相應(yīng)修改。
7. 【強制】URL外部重定向傳入的目標(biāo)地址必須執(zhí)行白名單過濾。
正例:
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應(yīng)用必須正確配置Robots文件,非SEO URL必須配置為禁止爬蟲訪問。
User-agent: * Disallow: /
9. 【強制】在使用平臺資源,譬如短信、郵件、電話、下單、支付,必須實現(xiàn)正確的防重放限制,如數(shù)量限制、疲勞度控制、驗證碼校驗,避免被濫刷、資損。
說明:如注冊時發(fā)送驗證碼到手機,如果沒有限制次數(shù)和頻率,那么可以利用此功能騷擾到其它用戶,并造成短信平臺資源浪費。
10.【推薦】發(fā)貼、評論、發(fā)送即時消息等用戶生成內(nèi)容的場景必須實現(xiàn)防刷、文本內(nèi)容違禁詞過濾等風(fēng)控策略。
建議下載官方的pdf格式更利于學(xué)習(xí)與閱讀:http://www.dbjr.com.cn/books/592610.html
相關(guān)文章
springMvc注解之@ResponseBody和@RequestBody詳解
本篇文章主要介紹了springMvc注解之@ResponseBody和@RequestBody詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Spring實戰(zhàn)之Bean定義中的SpEL表達(dá)式語言支持操作示例
這篇文章主要介紹了Spring實戰(zhàn)之Bean定義中的SpEL表達(dá)式語言支持操作,結(jié)合實例形式分析了Bean定義中的SpEL表達(dá)式語言操作步驟與實現(xiàn)技巧,需要的朋友可以參考下2019-12-12SpringBoot中@Scheduled實現(xiàn)服務(wù)啟動時執(zhí)行一次
本文主要介紹了SpringBoot中@Scheduled實現(xiàn)服務(wù)啟動時執(zhí)行一次,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08Java for循環(huán)性能優(yōu)化實現(xiàn)解析
這篇文章主要介紹了Java for循環(huán)性能優(yōu)化實現(xiàn)解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-01-01java.imageIo給圖片添加水印的實現(xiàn)代碼
最近項目在做一個商城項目, 項目上的圖片要添加水?、?添加圖片水印;②:添加文字水印;一下提供下個方法,希望大家可以用得著2013-07-07Spring@Autowired與@Resource的區(qū)別有哪些
這篇文章主要為大家詳細(xì)介紹了@Autowired與@Resource的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02