編程規(guī)約
命名風格
1.代碼命名不能以下劃線或者美元符號開頭或者結(jié)尾2.代碼命名不能以中文拼音或者中文拼音與英文混合方式3.類名使用UpperCamCamelCase風格,但DO、PO、DTO、VO、BO等除外4.方法名、參數(shù)名、變量名統(tǒng)一使用lowerCamelCase,必須遵守駝峰命名5.常量名全部大寫,單詞間用下劃線隔開6.抽象類必須以Abstract或者Base開頭,異常類必須以Exception結(jié)尾,測試? ? ? ? ? ? ? 類以測試的類的名稱開頭Test結(jié)尾7.類型與中括號緊挨相連標示數(shù)組8.POJO類中布爾類型變量不要加is前綴9.包名統(tǒng)一小寫,點分隔符有且有一個自然語義單詞10.避免在父子類和不同代碼塊中采用相同變量名11.避免不規(guī)范的縮寫命名12.在對元素命名時用完整單詞組合表達其意13.常量和變量命名時,表示類型放在詞尾,如:idList、TERMINATED_TREAD_COUNT14.接口、類、方法、模塊使用設計模式,命名時要體現(xiàn)具體模式15.接口類中的方法和屬性不要加任何修飾符,并加上有效的javadoc。16.接口和實現(xiàn)類的命名規(guī)則:1、對于service和dao類,實現(xiàn)類必須用Impl結(jié)尾;2、如果是形容能力的接口名稱,取對應的形容詞為接口名 AbstractTranslator實現(xiàn) Translatable接口17.枚舉類名加Enum后綴,枚舉成員名稱全大寫,單詞間用下劃線隔開18.各層命名規(guī)范:A)Service/DAO層命名規(guī)約1.獲取單個對象的方法用get做前綴2.獲取多個對象的方法用list做前綴,如:listObjects3.獲取統(tǒng)計值的方法用count做前綴4.插入方法用save/insert做前綴5.刪除方法用delete/remove做前綴6.修改方法用update做前綴B)領域模型命名規(guī)范1.數(shù)據(jù)對象:xxxDO,xxx為數(shù)據(jù)庫表名2.數(shù)據(jù)傳輸對象:xxxDTO,xxx為業(yè)務模型相關名稱3.展示對象:xxxVO,xxx一般為網(wǎng)頁名稱4.POJO是對DO、DTO、VO、BO的統(tǒng)稱,禁止xxxPOJO
常量定義
1.代碼中禁止出現(xiàn)魔法值2.在Long類型中賦值,數(shù)值后使用大寫L3.不要在一個常量類中維護所有常量,要根據(jù)功能分開維護4.常量的復用層次:1.跨應用:放在二方庫中,通常在constant目錄下2.應用內(nèi):放在一方庫中,通常在constant目錄下3.子工程內(nèi):放在當前子工程constant目錄下4.包內(nèi)共享常量:當前包下單獨的constant目錄下5.類內(nèi)共享常量:直接在類內(nèi)部privatestaticfinal定義5.如果變量值只在固定的范圍內(nèi)變化,用enum類型定義
代碼格式
1.如果大括號代碼為空直接'{}',大括號內(nèi)有代碼則:左大括號左側(cè)不換行,右側(cè)換行;右大括號右側(cè)換行,左側(cè)如果不跟else等代碼換行,否則不換行2.小括號和字符之間不能有空格,括號內(nèi)字符和運算符之間有空格 如:if(a==b)3.if、for、while、do、switch與括號之間必須有空格4.任何二目、三目運算符前后必須有空格5.采用4個空格,禁止使用tab6.注釋的雙斜線和內(nèi)容要有空格7.強制類型轉(zhuǎn)換時,右括號與強制轉(zhuǎn)換值之間不用空格8.單行字符不超過120個,超過要換行9.方法在定義和傳參時,必須要加空格10.IDE的text file encoding 設置為UTF-8;IDE中 文件的換行符使用Unix格式11.單個方法盡量不超過80行12.不同邏輯、不同語義、不同業(yè)務之間的代碼插入一個空行分隔符
OOP規(guī)約
1.不用一個類型的對象引用來訪問靜態(tài)方法和靜態(tài)屬性,直接類名訪問即可2.所有覆寫方法,必須加@Override注解3.相同業(yè)務含義,相同參數(shù)類型才能使用java可變參數(shù)4.外部依賴或者二方庫依賴的接口,不能修改方法簽名。接口過時必須用@Deprecated 注解,并說明新接口或者新服務是什么5.不能使用過時的類或者方法6.Object的equals方法容易拋出空指針,應使用常量或者確定值的對象來調(diào)用equals7.所有整型包裝類之間的值比較都用equals 方法比較8.浮點數(shù)之間的等值判斷,基本類型不能用==,包裝類不能用equals。? ? 解決方案:(1)指定一個誤差范圍,兩個浮點數(shù)的差值在此范圍之內(nèi),則認為是相等的。(2)使用BigDecimal來定義值,再進行浮點數(shù)的運算操作。9.定義DO類時,屬性類型要數(shù)據(jù)庫字段類型相匹配10.防止精度丟失,禁止使用BigDecimal(double)方式將double對象轉(zhuǎn)換成BigDecimal。建議使用BigDecimal的valueOf方法11.基本類型和包裝類型的使用標準1.所有POJO的屬性必須用包裝類型2.RPC方法的參數(shù)和返回值必須使用包裝類型3.所有局部變量使用基本變量12.所有POJO 不要對其屬性設置默認值13.序列化類新增時不要修改其serialVersionUID字段14.構(gòu)造方法里禁止加任何業(yè)務處理邏輯,有要加在init()15.POJO類必須要寫toString方法16.禁止在POJO類中對屬性xxx 同時存在isXxx()和getXxx()17.使用索引訪問用String的split方法得到數(shù)組時,需要對最后一個分隔符有無內(nèi)容做檢查18.一個類有多個構(gòu)造方法或者多個同名方法,要按照順序來。19.類中的方法順序 :共有方法->私有方法->get/set20.setter方法中參數(shù)名稱和成員變量名稱一致,不要在getter和setter方法中加業(yè)務邏輯21.循環(huán)體內(nèi)用StringBuilder的append方法進行擴展22.final可以修飾類,方法,變量。23.慎用Object的clone方法24.類成員與方法訪問控制從嚴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。
集合處理
1.hashCode和equals 的處理遵循以下規(guī)則:1)只要覆寫equals ,就必須要覆寫hashCode2)因為Set存儲的是不重復的對象,依據(jù)hashCode和equals進行判斷,所以Set存儲的對象必須覆寫這兩個方法。3)如果自定義對象作為Map的鍵,那么必須覆寫hashCode和equals。2.ArrayList的subList結(jié)果不能強轉(zhuǎn)ArrayList。3.使用map的keySet()、values()、entrySet()方法返回對象后不可以對其進行添加元素的操作4.Collections類返回的對象,如:emptyList()/singletonList()等都是immutablelist不可對其進行添加或者刪除元素的操作5.在subList場景中,高度注意對原集合元素的增加或刪除,均會導致子列表的遍歷、增加、刪除產(chǎn)生ConcurrentModificationException 異常6.使用集合轉(zhuǎn)數(shù)組的方法,必須使用集合的toArray(T[]array),傳入的是類型完全一致、長度為0的空數(shù)組7.在使用Collection接口任何實現(xiàn)類的addAll()方法時,一定要對輸入的集合做NEP判斷8.使用工具類Arrays.asList()把數(shù)組轉(zhuǎn)換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常? ? 說明:asList的返回對象是一個Arrays內(nèi)部類,并沒有實現(xiàn)集合的修改方法。Arrays.asList體現(xiàn)的是適配器模式,只是轉(zhuǎn)換接口,后臺的數(shù)據(jù)仍是數(shù)組。9.泛型通配符<?extends T>來接收返回的數(shù)據(jù),此寫法的泛型集合不能使用add方法,而<?super T>不能使用get方法,作為接口調(diào)用賦值時易出錯10.在無泛型限制定義的集合賦值給泛型限制的集合時,在使用集合元素時,需要進行instanceof判斷,避免拋出ClassCastException異常11.不要在foreach循環(huán)里進行元素的remove/add操作。remove元素請使用Iterator方式,如果并發(fā)操作,需要對Iterator對象加鎖12.在JDK7 版本及以上,Comparator 實現(xiàn)類要滿足如下三個條件,不然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é)果相同。13.集合泛型定義時,在JDK7 及以上,使用diamond 語法或全省略。14.集合初始化時,指定集合初始值大小。15.使用entrySet 遍歷Map 類集合KV,而不是keySet 方式進行遍歷16.高度注意Map類集合K/V能不能存儲null值的情況,如下表格:

image.png
17.合理利用好集合的有序性(sort)和穩(wěn)定性(order),避免集合的無序性(unsort)和不穩(wěn)定性(unorder)帶來的負面影響。18.利用Set元素唯一的特性,可以快速對一個集合進行去重操作,避免使用List的contains方法進行遍歷、對比、去重操作
并發(fā)處理
1.獲取單例對象需要保證線程安全,其中的方法也要保證線程安全。2.創(chuàng)建線程或線程池時請指定有意義的線程名稱,方便出錯時回溯。3.線程資源必須通過線程池提供,不允許在應用中自行顯式創(chuàng)建線程4.線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風險5.SimpleDateFormat 是線程不安全的類,一般不要定義為static變量,如果定義為static,必須加鎖,或者使用DateUtils工具類。6.必須回收自定義的ThreadLocal變量,尤其在線程池場景下,線程經(jīng)常會被復用,如果不清理自定義的 ThreadLocal變量,可能會影響后續(xù)業(yè)務邏輯和造成內(nèi)存泄露等問題。盡量在代理中使用try-finally塊進行回收7.高并發(fā)時,同步調(diào)用應該去考量鎖的性能損耗。能用無鎖數(shù)據(jù)結(jié)構(gòu),就不要用鎖;能鎖區(qū)塊,就不要鎖整個方法體;能用對象鎖,就不要用類鎖8.對多個資源、數(shù)據(jù)庫表、對象同時加鎖時,需要保持一致的加鎖順序,否則可能會造成死鎖。9.在使用阻塞等待獲取鎖的方式中,必須在try代碼塊之外,并且在加鎖方法與try代碼塊之間沒有任何可能拋出異常的方法調(diào)用,避免加鎖成功后,在finally中無法解鎖。10.在使用嘗試機制來獲取鎖的方式中,進入業(yè)務代碼塊之前,必須先判斷當前線程是否持有鎖。鎖的釋放規(guī)則與鎖的阻塞等待方式相同11.并發(fā)修改同一記錄時,避免更新丟失,需要加鎖。要么在應用層加鎖,要么在緩存加鎖,要么在數(shù)據(jù)庫層使用樂觀鎖,使用version作為更新依據(jù)12.多線程并行處理定時任務時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務便會自動終止運行,如果在處理定時任務時使用ScheduledExecutorService則沒有這個問題13.資金相關的金融敏感信息,使用悲觀鎖策略14.使用CountDownLatch進行異步轉(zhuǎn)同步操作,每個線程退出前必須調(diào)用countDown方法,線程執(zhí)行代碼注意catch異常,確保countDown方法被執(zhí)行到,避免主線程無法執(zhí)行至await方法,直到超時才返回結(jié)果15.避免Random實例被多線程使用,雖然共享該實例是線程安全的,但會因競爭同一seed 導致的性能下降16.在并發(fā)場景下,通過雙重檢查鎖(double-checkedlocking)實現(xiàn)延遲初始化的優(yōu)化問題隱患(可參考 The"Double-Checked Locking is Broken"Declaration),推薦解決方案中較為簡單一種(適用于JDK5及以上版本),將目標屬性聲明為volatile型17.volatile解決多線程內(nèi)存不可見問題。對于一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。18.HashMap在容量不夠進行resize時由于高并發(fā)可能出現(xiàn)死鏈,導致CPU飆升,在開發(fā)過程中可以使用其它數(shù)據(jù)結(jié)構(gòu)或加鎖來規(guī)避此風險19.ThreadLocal對象使用static修飾,ThreadLocal無法解決共享對象的更新問題
控制語句
1.在一個switch塊內(nèi),每個case要么通過continue/break/return等來終止,要么注釋說明程序?qū)⒗^續(xù)執(zhí)行到哪一個case為止;在一個switch塊內(nèi),都必須包含一個default語句并且放在最后,即使它什么代碼也沒有2.當switch括號內(nèi)的變量類型為String并且此變量為外部參數(shù)時,必須先進行null判斷3.在if/else/for/while/do語句中必須使用大括號4.在高并發(fā)場景中,避免使用”等于”判斷作為中斷或退出的條件5.表達異常的分支時,少用if-else方式6.除常用方法(如getXxx/isXxx)等外,不要在條件判斷中執(zhí)行其它復雜的語句,將復雜邏輯判斷的結(jié)果賦值給一個有意義的布爾變量名,以提高可讀性7.不要在其它表達式(尤其是條件表達式)中,插入賦值語句8.循環(huán)體中的語句要考量性能,以下操作盡量移至循環(huán)體外處理,如定義對象、變量、獲取數(shù)據(jù)庫連接,進行不必要的try-catch操作(這個try-catch是否可以移至循環(huán)體外)。9.避免采用取反邏輯運算符10.接口入?yún)⒈Wo,這種場景常見的是用作批量操作的接口11.下列情形,需要進行參數(shù)校驗:1) 調(diào)用頻次低的方法。2) 執(zhí)行時間開銷很大的方法。此情形中,參數(shù)校驗時間幾乎可以忽略不計,但如果因為參數(shù)錯誤導致 中間執(zhí)行回退,或者錯誤,那得不償失。3) 需要極高穩(wěn)定性和可用性的方法。4) 對外提供的開放接口,不管是RPC/API/HTTP接口。5) 敏感權限入口。12.下列情形,不需要進行參數(shù)校驗:1) 極有可能被循環(huán)調(diào)用的方法。但在方法說明里必須注明外部參數(shù)檢查要求。2) 底層調(diào)用頻度比較高的方法。畢竟是像純凈水過濾的最后一道,參數(shù)錯誤不太可能到底層才會暴露問題。一般DAO層與Service層都在同一個應用中,部署在同一臺服務器中,所以DAO的參數(shù)校驗,可以省略。3) 被聲明成private只會被自己代碼所調(diào)用的方法,如果能夠確定調(diào)用方法的代碼傳入?yún)?shù)已經(jīng)做過檢查或者肯定不會有問題,此時可以不校驗參數(shù)。
注釋規(guī)范
1.類、類屬性、類方法的注釋必須使用Javadoc規(guī)范,使用/**內(nèi)容*/格式,不得使用// xxx方式2.所有的抽象方法(包括接口中的方法)必須要用Javadoc注釋、除了返回值、參數(shù)、異常說明外,還必須指出該方法做什么事情,實現(xiàn)什么功能3.所有的類都必須添加創(chuàng)建者和創(chuàng)建日期4.方法內(nèi)部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內(nèi)部多行注釋使用/* */注釋,注意與代碼對齊5.所有的枚舉類型字段必須要有注釋,說明每個數(shù)據(jù)項的用途6.與其“半吊子”英文來注釋,不如用中文注釋把問題說清楚。專有名詞與關鍵字保持英文原文即可。7.代碼修改的同時,注釋也要進行相應的修改,尤其是參數(shù)、返回值、異常、核心邏輯等的修改8.謹慎注釋掉代碼。在上方詳細說明,而不是簡單地注釋掉。如果無用,則刪除。9.對于注釋的要求:第一、能夠準確反映設計思想和代碼邏輯;第二、能夠描述業(yè)務含義,使別的程序員能夠迅速了解到代碼背后的信息。完全沒有注釋的大段代碼對于閱讀者形同天書,注釋是給自己看的,即使隔很長時間,也能清晰理解當時的思路;注釋也是給繼任者看的,使其能夠快速接替自己的工作。10.好的命名、代碼結(jié)構(gòu)是自解釋的,注釋力求精簡準確、表達到位。避免出現(xiàn)注釋的一個極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當大的負擔11.特殊注釋標記,請注明標記人與標記時間。注意及時處理這些標記,通過標記掃描,經(jīng)常清理此類標記。線上故障有時候就是來源于這些標記處的代碼
其他
1.在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度2.velocity調(diào)用POJO類的屬性時,直接使用屬性名取值即可,模板引擎會自動按規(guī)范調(diào)用POJO的getXxx(),如果是boolean基本數(shù)據(jù)類型變量(boolean命名不需要加is前綴),會自動調(diào)用isXxx()方法3.后臺輸送給頁面的變量必須加$!{var}——中間的感嘆號4.注意 Math.random()這個方法返回是double類型,注意取值的范圍0≤x<1(能夠取到零值,注意除零異常),如果想獲取整數(shù)類型的隨機數(shù),不要將x放大10的若干倍然后取整,直接使用Random對象的nextInt或者nextLong方法5.獲取當前毫秒數(shù)System.currentTimeMillis();而不是newDate().getTime();6.日期格式化時,傳入pattern中表示年份統(tǒng)一使用小寫的y7.不要在視圖模板中加入任何復雜的邏輯8.任何數(shù)據(jù)結(jié)構(gòu)的構(gòu)造或初始化,都應指定大小,避免數(shù)據(jù)結(jié)構(gòu)無限增長吃光內(nèi)存9.及時清理不再使用的代碼段或配置信息
異常日志
異常處理
1.java類庫中定義的可以通過預檢查方式規(guī)避的RuntimeException異常不應該通過catch方式處理。如NullPointException、IndexOutOfBoundsException。2.異常不要用作流程控制、條件控制。3.catch是要分清是穩(wěn)定代碼和非穩(wěn)定代碼,對于非穩(wěn)定代碼catch盡可能的按照異常類型分類。4.捕獲異常一定要做處理,如果不想處理就拋給上層調(diào)用者。5.有try塊放在事務中,catch異常后如果需要回滾事務,一定要注意手動回滾事務。6.finally塊中必須對資源對象、流對象進行關閉,有異常也要catch。7.不要在finally塊中使用return8.捕獲的異常要和拋的異常匹配或者捕獲的異常是拋異常的父類9.在調(diào)用RPC、二方包、或動態(tài)生成類的相關方法時,捕獲異常一定要用Throwable類攔截10.方法的返回值可以是null,但是必須要說明什么情況返回null11.防止NEP:1.返回類型是基本類型 ,return包裝類型的對象。2.數(shù)據(jù)庫查詢的結(jié)果可能是null3.集合里的元素即時isNotEmpty,取出來的元素也可能是null4.遠程調(diào)用返回對象時,必須要進行判空處理5.對于Session中的數(shù)據(jù)要進行判空處理6.級聯(lián)調(diào)用有可能產(chǎn)生空指針12.定義時區(qū)分unchecked/checked 異常,避免直接拋出newRuntimeException(),更不允許拋出Exception或者Throwable,應使用有業(yè)務含義的自定義異常。推薦業(yè)界已定義過的自定義異常,如:DAOException/ServiceException等13.對于公司外的http/api開放接口必須使用“錯誤碼”;而應用內(nèi)部推薦異常拋出;跨應用間RPC調(diào)用優(yōu)先考慮使用Result方式,封裝isSuccess()方法、“錯誤碼”、“錯誤簡短信息”14.避免出現(xiàn)重復的代碼(Don't Repeat Yourself),即DRY原則
日志規(guī)約
1.應用中不可直接使用日志系統(tǒng)(Log4j、Logback)中的API,而應依賴使用日志框架 SLF4J中的API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統(tǒng)一2.所有日志文件至少保存15天,因為有些異常具備以“周”為頻次發(fā)生的特點。網(wǎng)絡運行狀態(tài)、安全相關信息、系統(tǒng)監(jiān)測、管理后臺操作、用戶敏感操作需要留存相關的網(wǎng)絡日志不少于6個月3.應用中的擴展日志(如打點、臨時監(jiān)控、訪問日志等)命名方式:appName_logType_logName.log。logType:日志類型,如stats/monitor/access等;logName:日志描述。這種命名的好處:通過文件名就可知道日志文件屬于什么應用,什么類型,什么目的,也有利于歸類查找4.在日志輸出時,字符串變量之間的拼接使用占位符的方式5.對于trace/debug/info級別的日志輸出,必須進行日志級別的開關判斷6.避免重復打印日志,浪費磁盤空間,務必在log4j.xml中設置additivity=false7.異常信息應該包括兩類信息:案發(fā)現(xiàn)場信息和異常堆棧信息。如果不處理,那么通過關鍵字throws往上拋出8.謹慎地記錄日志。生產(chǎn)環(huán)境禁止輸出debug日志;有選擇地輸出info日志;如果使用warn來記錄剛上線時的業(yè)務行為信息,一定要注意日志輸出量的問題,避免把服務器磁盤撐爆,并記得及時刪除這些觀察日志9.可以使用warn日志級別來記錄用戶輸入?yún)?shù)錯誤的情況,避免用戶投訴時,無所適從。如非必要,請不要在此場景打出error級別,避免頻繁報警10.盡量用英文來描述日志錯誤信息,如果日志中的錯誤信息用英文描述不清楚的話使用中文描述即可,否則容易產(chǎn)生歧義?!緩娭啤繃H化團隊或海外部署的服務器由于字符集問題,使用全英文來注釋和描述日志錯誤信息
單元測試
1. 好的單元測試必須遵守AIR原則 2. 單元測試應該是全自動執(zhí)行的,并且非交互式的。測試用例通常是被定期執(zhí)行的,執(zhí)行過程必須完全自動化才有意義。輸出結(jié)果需要人工檢查的測試不是一個好的單元測試。單元測試中不準使用System.out來進行人肉驗證,必須使用assert來驗證 3. 保持單元測試的獨立性。為了保證單元測試穩(wěn)定可靠且便于維護,單元測試用例之間決不能互相調(diào)用,也不能依賴執(zhí)行的先后次序 4. 單元測試是可以重復執(zhí)行的,不能受到外界環(huán)境的影響 5. 對于單元測試,要保證測試粒度足夠小,有助于精確定位問題。單測粒度至多是類級別,一般是方法級別 6. 核心業(yè)務、核心應用、核心模塊的增量代碼確保單元測試通過 7. 單元測試代碼必須寫在如下工程目錄:src/test/java,不允許寫在業(yè)務代碼目錄下 8. 單元測試的基本目標:語句覆蓋率達到70%;核心模塊的語句覆蓋率和分支覆蓋率都要達到100% 9. 編寫單元測試代碼遵守BCDE原則,以保證被測試模塊的交付質(zhì)量 10. 對于數(shù)據(jù)庫相關的查詢,更新,刪除等操作,不能假設數(shù)據(jù)庫里的數(shù)據(jù)是存在的,或者直接操作數(shù)據(jù)庫把數(shù)據(jù)插入進去,請使用程序插入或者導入數(shù)據(jù)的方式來準備數(shù)據(jù) 11. 和數(shù)據(jù)庫相關的單元測試,可以設定自動回滾機制,不給數(shù)據(jù)庫造成臟數(shù)據(jù)?;蛘邔卧獪y試產(chǎn)生的數(shù)據(jù)有明確的前后綴標識 12. 對于不可測的代碼在適當?shù)臅r機做必要的重構(gòu),使代碼變得可測,避免為了達到測試要求而書寫不規(guī)范測試代碼 13. 在設計評審階段,開發(fā)人員需要和測試人員一起確定單元測試范圍,單元測試最好覆蓋所有測試用例 14. 單元測試作為一種質(zhì)量保障手段,在項目提測前完成單元測試,不建議項目發(fā)布后補充單元測試用例 15. 為了更方便地進行單元測試,業(yè)務代碼應避免以下情況: 1.構(gòu)造方法中做的事情過多。 2. 存在過多的全局變量和靜態(tài)方法。 3. 存在過多的外部依賴。 4. 存在過多的條件語句 16. 那是測試同學干的事情。本文是開發(fā)手冊,凡是本文內(nèi)容都是與開發(fā)同學強相關的。 1. 單元測試代碼是多余的。系統(tǒng)的整體功能與各單元部件的測試正常與否是強相關的。 2. 單元測試代碼不需要維護。一年半載后,那么單元測試幾乎處于廢棄狀態(tài)。 3. 單元測試與線上故障沒有辯證關系。好的單元測試能夠最大限度地規(guī)避線上故障
安全規(guī)約
1. 隸屬于用戶個人的頁面或者功能必須進行權限控制校驗 2. 用戶敏感數(shù)據(jù)禁止直接展示,必須對展示數(shù)據(jù)進行脫敏 3. 用戶輸入的SQL參數(shù)嚴格使用參數(shù)綁定或者METADATA字段值限定,防止SQL注入,禁止字符串拼接SQL訪問數(shù)據(jù)庫 4. 用戶請求傳入的任何參數(shù)必須做有效性驗證 5. 禁止向HTML頁面輸出未經(jīng)安全過濾或未正確轉(zhuǎn)義的用戶數(shù)據(jù) 6. 表單、AJAX提交必須執(zhí)行CSRF安全驗證 7. 在使用平臺資源,譬如短信、郵件、電話、下單、支付,必須實現(xiàn)正確的防重放的機制,如數(shù)量限制、疲勞度控制、驗證碼校驗,避免被濫刷而導致資損 8. 發(fā)貼、評論、發(fā)送即時消息等用戶生成內(nèi)容的場景必須實現(xiàn)防刷、文本內(nèi)容違禁詞過濾等風控策略
MySQL數(shù)據(jù)庫
建表規(guī)約
1.表達是與否概念的字段,必須使用is_xxx的方式命名,數(shù)據(jù)類型是unsignedtinyint(1表示是,0表示否)2.表名、字段名必須使用小寫字母或數(shù)字,禁止出現(xiàn)數(shù)字開頭,禁止兩個下劃線中間只出現(xiàn)數(shù)字。數(shù)據(jù)庫字段名的修改代價很大,因為無法進行預發(fā)布,所以字段名稱需要慎重考慮3.表名不使用復數(shù)名詞4.禁用保留字,如desc、range、match、delayed等,請參考MySQL官方保留字5.主鍵索引名為pk_字段名;唯一索引名為uk_字段名;普通索引名則為idx_字段名6.小數(shù)類型為decimal,禁止使用float和double7.如果存儲的字符串長度幾乎相等,使用char定長字符串類型8.varchar是可變長字符串,不預先分配存儲空間,長度不要超過5000,如果存儲長度大于此值,定義字段類型為text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率9.表必備三字段:id,create_time,update_time10.表的命名最好是遵循“業(yè)務名稱_表的作用”11.庫名與應用名稱盡量一致12.字段允許適當冗余,以提高查詢性能,但必須考慮數(shù)據(jù)一致。冗余字段應遵循:1) 不是頻繁修改的字段。2) 不是varchar超長字段,更不能是text字段。3) 不是唯一索引的字段。13.如果修改字段含義或?qū)ψ侄伪硎镜臓顟B(tài)追加時,需要及時更新字段注釋14.單表行數(shù)超過500萬行或者單表容量超過2GB,才推薦進行分庫分表15.合適的字符存儲長度,不但節(jié)約數(shù)據(jù)庫表空間、節(jié)約索引存儲,更重要的是提升檢索速度
索引規(guī)約
1.業(yè)務上具有唯一特性的字段,即使是多個字段的組合,也必須建成唯一索引2.超過三個表禁止join。需要join的字段,數(shù)據(jù)類型必須絕對一致;多表關聯(lián)查詢時,保證被關聯(lián)的字段需要有索引3.在varchar字段上建立索引時,必須指定索引長度,沒必要對全字段建立索引,根據(jù)實際文本區(qū)分度決定索引長度即可4.頁面搜索嚴禁左模糊或者全模糊,如果需要請走搜索引擎來解決5.如果有order by的場景,請注意利用索引的有序性。order by 最后的字段是組合索引的一部分,并且放在索引組合順序的最后,避免出現(xiàn)file_sort的情況,影響查詢性能6.利用覆蓋索引來進行查詢操作,避免回表7.利用延遲關聯(lián)或者子查詢優(yōu)化超多分頁場景8.SQL性能優(yōu)化的目標:至少要達到 range 級別,要求是ref級別,如果可以是consts最好9.建組合索引的時候,區(qū)分度最高的在最左邊10.防止因字段類型不同造成的隱式轉(zhuǎn)換,導致索引失效11.創(chuàng)建索引時避免有如下極端誤解:1) 寧濫勿缺。認為一個查詢就需要建一個索引。2) 寧缺勿濫。認為索引會消耗空間、嚴重拖慢記錄的更新以及行的新增速度。3) 抵制惟一索引。認為業(yè)務的惟一性一律需要在應用層通過“先查后插”方式解決。
SQL語句
1.不要使用count(列名)或count(常量)來替代count(*),count(*)是SQL92定義的標準統(tǒng)計行數(shù)的語法,跟數(shù)據(jù)庫無關,跟NULL和非NULL無關2.count(distinct col)計算該列除NULL之外的不重復行數(shù),注意count(distinct col1,col2)如果其中一列全為NULL,那么即使另一列有不同的值,也返回為03.當某一列的值全是NULL時,count(col)的返回結(jié)果為0,但sum(col)的返回結(jié)果為NULL,因此使用sum()時需注意NPE問題4.使用ISNULL()來判斷是否為NULL值5.代碼中寫分頁查詢邏輯時,若count為0應直接返回,避免執(zhí)行后面的分頁語句6.不得使用外鍵與級聯(lián),一切外鍵概念必須在應用層解決7.禁止使用存儲過程,存儲過程難以調(diào)試和擴展,更沒有移植性8.數(shù)據(jù)訂正(特別是刪除、修改記錄操作)時,要先select,避免出現(xiàn)誤刪除,確認無誤才能執(zhí)行更新語句9.in操作能避免則避免,若實在避免不了,需要仔細評估in后邊的集合元素數(shù)量,控制在1000個之內(nèi)10.如果有國際化需要,所有的字符存儲與表示,均以utf-8編碼,注意字符統(tǒng)計函數(shù)的區(qū)別11.TRUNCATETABLE比DELETE速度快,且使用的系統(tǒng)和事務日志資源少,但TRUNCATE無事務且不觸發(fā)trigger,有可能造成事故,故不建議在開發(fā)代碼中使用此語句
ORM映射
1.在表查詢中,一律不要使用*作為查詢的字段列表,需要哪些字段必須明確寫明2.POJO類的布爾屬性不能加is,而數(shù)據(jù)庫字段必須加is_,要求在resultMap中進行字段與屬性之間的映射3.不要用resultClass當返回參數(shù),即使所有類屬性名與數(shù)據(jù)庫字段一一對應,也需要定義;反過來,每一個表也必然有一個POJO類與之對應4.sql.xml配置參數(shù)使用:#{},#param# 不要使用${}此種方式容易出現(xiàn)SQL注入5.iBATIS自帶的queryForList(String statementName,int start,int size)不推薦使用6.不允許直接拿HashMap與Hashtable作為查詢結(jié)果集的輸出7.更新數(shù)據(jù)表記錄時,必須同時更新記錄對應的gmt_modified字段值為當前時間8.不要寫一個大而全的數(shù)據(jù)更新接口。傳入為POJO類,不管是不是自己的目標更新字段,都進行update tablesetc1=value1,c2=value2,c3=value3;這是不對的。執(zhí)行SQL時,不要更新無改動的字段,一是易出錯;二是效率低;三是增加binlog存儲9.@Transactional事務不要濫用。事務會影響數(shù)據(jù)庫的QPS,另外使用事務的地方需要考慮各方面的回滾方案,包括緩存回滾、搜索引擎回滾、消息補償、統(tǒng)計修正等10.<isEqual>中的compareValue是與屬性值對比的常量,一般是數(shù)字,表示相等時帶上此條件;<isNotEmpty>表示不為空且不為null時執(zhí)行;<isNotNull>表示不為null值時執(zhí)行
工程結(jié)構(gòu)
應用分層
1. 圖中默認上層依賴于下層,箭頭關系表示可直接依賴,如:開放接口層可以依賴于Web層,也可以直接依賴于Service層,依此類推

image.png
? 開放接口層:可直接封裝Service方法暴露成RPC接口;通過Web封裝成http接口;進行網(wǎng)關安全控制、流量控制等。
? 終端顯示層:各個端的模板渲染并執(zhí)行顯示的層。當前主要是velocity渲染,JS渲染,JSP渲染,移動端展示等。
? Web層:主要是對訪問控制進行轉(zhuǎn)發(fā),各類基本參數(shù)校驗,或者不復用的業(yè)務簡單處理等。
? Service層:相對具體的業(yè)務邏輯服務層。
? Manager層:通用業(yè)務處理層,它有如下特征: 1) 對第三方平臺封裝的層,預處理返回結(jié)果及轉(zhuǎn)化異常信息。 2) 對Service層通用能力的下沉,如緩存方案、中間件通用處理。 3) 與DAO層交互,對多個DAO的組合復用。
? DAO層:數(shù)據(jù)訪問層,與底層MySQL、Oracle、Hbase等進行數(shù)據(jù)交互。
? 外部接口或第三方平臺:包括其它部門RPC開放接口,基礎平臺,其它公司的HTTP接口。
2.(分層異常處理規(guī)約)在DAO層,產(chǎn)生的異常類型有很多,無法用細粒度的異常進行catch,使用catch(Exception e)方式,并thrownewDAOException(e),不需要打印日志,因為日志在Manager/Service層一定需要捕獲并打印到日志文件中去,如果同臺服務器再打日志,浪費性能和存儲。在Service層出現(xiàn)異常時,必須記錄出錯日志到磁盤,盡可能帶上參數(shù)信息,相當于保護案發(fā)現(xiàn)場。如果Manager層與Service同機部署,日志方式與DAO層處理一致,如果是單獨部署,則采用與Service一致的處理方式。Web層絕不應該繼續(xù)往上拋異常,因為已經(jīng)處于頂層,如果意識到這個異常將導致頁面無法正常渲染,那么就應該直接跳轉(zhuǎn)到友好錯誤頁面,加上用戶容易理解的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。3.? DO(Data Object):此對象與數(shù)據(jù)庫表結(jié)構(gòu)一一對應,通過DAO層向上傳輸數(shù)據(jù)源對象。? ? ? DTO(Data Transfer Object):數(shù)據(jù)傳輸對象,Service或Manager向外傳輸?shù)膶ο蟆? ? ? BO(Business Object):業(yè)務對象,由Service層輸出的封裝業(yè)務邏輯的對象。? ? ? AO(Application Object):應用對象,在Web層與Service層之間抽象的復用對象模型,極為貼近展示層,復用度不高。? ? ? VO(View Object):顯示層對象,通常是Web向模板渲染引擎層傳輸?shù)膶ο蟆? ? ? Query:數(shù)據(jù)查詢對象,各層接收上層的查詢請求。注意超過2個參數(shù)的查詢封裝,禁止使用Map類來傳輸。
二方庫依賴
1.定義GAV遵從以下規(guī)則:1) GroupID格式:com.{公司/BU}.業(yè)務線[.子業(yè)務線],最多4級。 說明:{公司/BU}例如:alibaba/taobao/tmall/aliexpress等BU一級;子業(yè)務線可選。 正例:com.taobao.jstorm 或 com.alibaba.dubbo.register2) ArtifactID格式:產(chǎn)品線名-模塊名。語義不重復不遺漏,先到中央倉庫去查證一下。 正例:dubbo-client/fastjson-api/jstorm-tool3) Version:詳細規(guī)定參考下方2.二方庫版本號命名方式:主版本號.次版本號.修訂號1)主版本號:產(chǎn)品方向改變,或者大規(guī)模API不兼容,或者架構(gòu)不兼容升級。2) 次版本號:保持相對兼容性,增加主要功能特性,影響范圍極小的API不兼容修改。3) 修訂號:保持完全兼容性,修復BUG、新增次要功能特性等。3.線上應用不要依賴SNAPSHOT版本(安全包除外)4.二方庫的新增或升級,保持除功能點之外的其它jar包仲裁結(jié)果不變。如果有改變,必須明確評估和驗證5.二方庫里可以定義枚舉類型,參數(shù)可以使用枚舉類型,但是接口返回值不允許使用枚舉類型或者包含枚舉類型的POJO對象6.依賴于一個二方庫群時,必須定義一個統(tǒng)一的版本變量,避免版本號不一致7.禁止在子項目的pom依賴中出現(xiàn)相同的GroupId,相同的ArtifactId,但是不同的Version8.底層基礎技術框架、核心數(shù)據(jù)管理平臺、或近硬件端系統(tǒng)謹慎引入第三方實現(xiàn)9.所有pom文件中的依賴聲明放在<dependencies>語句塊中,所有版本仲裁放在<dependencyManagement>語句塊中10.二方庫不要有配置項,最低限度不要再增加配置項11.為避免應用二方庫的依賴沖突問題,二方庫發(fā)布者應當遵循以下原則:1)精簡可控原則。移除一切不必要的API和依賴,只包含 Service API、必要的領域模型對象、Utils類、常量、枚舉等。如果依賴其它二方庫,盡量是provided引入,讓二方庫使用者去依賴具體版本號;無log具體實現(xiàn),只依賴日志框架。2)穩(wěn)定可追溯原則。每個版本的變化應該被記錄,二方庫由誰維護,源碼在哪里,都需要能方便查到。除非用戶主動升級版本,否則公共二方庫的行為不應該發(fā)生變化。
服務器
1. 高并發(fā)服務器建議調(diào)小TCP協(xié)議的time_wait超時時間 2. 調(diào)大服務器所支持的最大文件句柄數(shù)(File Descriptor,簡寫為fd) 3.給JVM環(huán)境參數(shù)設置-XX:+HeapDumpOnOutOfMemoryError參數(shù),讓JVM碰到OOM場景時輸出dump信息 4. 在線上生產(chǎn)環(huán)境,JVM的Xms和Xmx設置一樣大小的內(nèi)存容量,避免在GC 后調(diào)整堆大小帶來的壓力 5. 服務器內(nèi)部重定向使用forward;外部重定向地址使用URL拼裝工具類來生成,否則會帶來URL維護不一致的問題和潛在的安全風險
設計規(guī)約
1.存儲方案和底層數(shù)據(jù)結(jié)構(gòu)的設計獲得評審一致通過,并沉淀成為文檔2.在需求分析階段,如果與系統(tǒng)交互的User超過一類并且相關的UserCase超過5個,使用用例圖來表達更加清晰的結(jié)構(gòu)化需求3.如果某個業(yè)務對象的狀態(tài)超過3個,使用狀態(tài)圖來表達并且明確狀態(tài)變化的各個觸發(fā)條件4.如果系統(tǒng)中某個功能的調(diào)用鏈路上的涉及對象超過3個,使用時序圖來表達并且明確各調(diào)用環(huán)節(jié)的輸入與輸出5.如果系統(tǒng)中模型類超過5個,并且存在復雜的依賴關系,使用類圖來表達并且明確類之間的關系6.如果系統(tǒng)中超過2個對象之間存在協(xié)作關系,并且需要表示復雜的處理流程,使用活動圖來表示7.需求分析與系統(tǒng)設計在考慮主干功能的同時,需要充分評估異常流程與業(yè)務邊界8.類在設計與實現(xiàn)時要符合單一原則9.謹慎使用繼承的方式來進行擴展,優(yōu)先使用聚合/組合的方式來實現(xiàn)10.系統(tǒng)設計時,根據(jù)依賴倒置原則,盡量依賴抽象類與接口,有利于擴展與維護11.系統(tǒng)設計時,注意對擴展開放,對修改閉合12.系統(tǒng)設計階段,共性業(yè)務或公共行為抽取出來公共模塊、公共配置、公共類、公共方法等,避免出現(xiàn)重復代碼或重復配置的情況13.避免如下誤解:敏捷開發(fā)=講故事+編碼+發(fā)布14.系統(tǒng)設計主要目的是明確需求、理順邏輯、后期維護,次要目的用于指導編碼15.設計的本質(zhì)就是識別和表達系統(tǒng)難點,找到系統(tǒng)的變化點,并隔離變化點16.系統(tǒng)架構(gòu)設計的目的:1.確定系統(tǒng)邊界。確定系統(tǒng)在技術層面上的做與不做。2.確定系統(tǒng)內(nèi)模塊之間的關系。確定模塊之間的依賴關系及模塊的宏觀輸入與輸出。3.確定指導后續(xù)設計與演化的原則。使后續(xù)的子系統(tǒng)或模塊設計在規(guī)定的框架內(nèi)繼續(xù)演化。4.確定非功能性需求。非功能性需求是指安全性、可用性、可擴展性等17.在做無障礙產(chǎn)品設計時,需要考慮到:1.所有可交互的控件元素必須能被tab鍵聚焦,并且焦點順序需符合自然操作邏輯。2.用于登陸校驗和請求攔截的驗證碼均需提供圖形驗證以外的其它方式。3.自定義的控件類型需明確交互方式。
作者:OPice
鏈接:http://www.itdecent.cn/p/1884cdc54409
來源:簡書
著作權歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權,非商業(yè)轉(zhuǎn)載請注明出處。