1、研發流程規範java
2、SQL編碼規範mysql
數據庫命名規範:數據庫名一概小寫,必須以字母開頭。庫名包含多個單詞的,如下劃線「_」分隔。若是採用分庫方案,分庫編號從「0」開始,用「0」左補齊爲四位。sql
表名規範:表名一概小寫,必須以字母開頭。表名中包含多個單詞的,如下劃線「_」分隔。若是採用分表方案,同時分表編號從「0」開始,用「0」左補齊爲四位。建議使用‘數據庫名_表名’形式,例如:tkn_users。數據庫
字段名和字段類型規範:字段名一概小寫,必須以字母開頭,言簡意賅且不含拼寫錯誤的單詞(限用有歧義的縮寫形式)。字段名中包含多個單詞的,以「_」分隔。字段類型越小越好,並留有必定餘地。字段類型儘可能設置爲not null(特別是primary key和unique key引用的字段,更要注意這一點);數字類型儘可能設置爲unsigned(防止溢出以後數值變負);不要保存default數值,以避免表結構中存在業務邏輯。primary key引用的字段,主表必須爲數字型非負非空自增id,分表必須爲數字型非負非空自增id或數字型非負非空id。注意約定俗成的字段,如user_id、gmt_create和gmt_modified。意義相同的狀況下,要使用約定俗成的字段。其中,gmt_create和gmt_modified字段須要定義爲datetime not null,user_id須要定義爲bigint unsigned。在表中使用擴展字段如features時,儘可能使用key-value形式存儲,每個key_value使用‘;’隔開,key所有小寫,必須以字母開頭,多個單詞使用'_'分割,不適用含有歧義的縮寫形式。apache
索引規範:不要使用含有null的列:只要列中包含有NULL值都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此複合索引就是無效的。因此咱們在數據庫設計時不要讓字段的默認值爲NULL。儘可能使用段索引:對串列進行索引,若是可能應該指定一個前綴長度。例如,若是有一個CHAR(255)的列,若是在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不只能夠提升查詢速度並且能夠節省磁盤空間和I/O操做。最好使用數字作索引。擴展索引:索引修改儘可能使用擴展索引,不要新建索引。好比表中已經有a的索引,如今要加(a,b)的索引,那麼只須要修改原來的索引便可。索引列排序:MySQL查詢只使用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。所以數據庫默認排序能夠符合要求的狀況下不要使用排序操做;儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引。like語句操做:通常狀況下不鼓勵使用like操做,若是非使用不可,如何使用也是一個問題。like 「%aaa%」 不會使用索引而like 「aaa%」可使用索引。不要在列上進行運算:select * from users where YEAR(adddate)<2007,將在每一個行上進行運算,這將致使索引失效而進行全表掃描,所以咱們能夠改爲:select * from users where adddate<’2007-01-01′。索引順序:mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。api
此外,分表查詢:分表查詢必須添加分表鍵做爲參數,以防止tddl對錶進行join查詢,如沒法使用分表鍵須要考慮分表鍵設置是否合理。單表數據量:單表數據量不超過500W,超過1000W的表需考慮使用分表,如沒法使用分表須要控制表記錄數,歷史數據考慮遷移。數組
3、異常處理規範app
在項目IDCM中,異常ServiceException繼承自RuntimeException。框架
package com.alibaba.tboss.exception; public class ServiceException extends RuntimeException { private static final long serialVersionUID = -91784960796452539L; protected String errCode; public ServiceException(String errorMsg){ super(errorMsg); } public ServiceException(String errorMsg, Exception e){ super(errorMsg, e); } public ServiceException(ErrorCode msgObj){ super(msgObj.getFullMsg()); this.errCode = msgObj.getCode(); } public ServiceException(ErrorCode msgObj, String... arg){ super(msgObj.getFullMsg(arg)); this.errCode = msgObj.getCode(); } public ServiceException(ErrorCode msgObj, Throwable cause){ super(msgObj.getFullMsg(), cause); this.errCode = msgObj.getCode(); } public String getErrCode() { return this.errCode; } public void setErrCode(String errCode) { this.errCode = errCode; } }
異常的整體原則:忽略不能處理的異常,留給框架統一處理。仔細定義業務異常,謹慎處理,不要吃掉任何異常。eclipse
數據訪問層:DAO中的方法能夠不聲明拋出異常(推薦)或聲明拋出DAOException(這個異常是SqlMapClientDaoSupportEx類封裝結果,沒有什麼做用)。數據訪問層通常不要定義業務異常。
//正確的聲明方式: CustomerSettings getCustomerSettingsById(long customerId); // 推薦 CustomerSettings getCustomerSettingsById(long customerId) throws DAOException; //不推薦的聲明方式:這樣會強制要求調用它的類捕獲這個異常處理 CustomerSettings getCustomerSettingsById(long customerId) throws Exception;
業務層:BO中的方法能夠不聲明拋出異常(推薦)或聲明拋出BOException或其餘的checked exception(業務異常)。對於業務異常,須要強制外部調用處理的,需合理規範和定義業務異常類。
//正確的聲明方式: public Long updateTrade(String tradeNo,String alitradeno,String totalFee); public Long updateTrade(String tradeNo,String alitradeno,String totalFee) throws BOException; //對於一些特殊的業務異常: public Long updateTrade(String tradeNo,String alitradeno,String totalFee) throws TradeException; public class TradeException extends Exception {...}
展示層:Controller中的方法能夠聲明拋出Exception,而且在處理過程當中不catch任何Exception。框架會捕獲並作後續操做,框架在捕到異常後,會打到root logger中,而且重定向到通用的錯誤頁面。若是你須要對特定的異常作特殊處理(如跳轉到其餘的錯誤頁面)的話,才須要考慮抓住異常,這時請正確地記錄這個異常,以方便後期跟蹤問題。
其餘規範:若是不是必定要處理,儘量忽略RuntimeException(包括DAOException和BOException等),留給框架處理。若是須要抓異常,儘可能抓某一個特定的異常(如TradeException),不要將全部Exception所有抓住。抓住異常後,要麼記錄異常詳細信息到日誌文件,要麼帶上原有異常從新拋出,不要兩樣都作,避免重複記錄。不要兩樣都不作,會吃掉異常。不要使用e.printStackTrace(),由於有性能問題,並且不能指定記錄日誌的文件。
// 不推薦這種方式:抓住異常log一下再拋出來,這是多餘的,框架會爲咱們處理。 // 固然,若是須要拋出的是checked exception則另當別論 try { .... } catch (Exception e) { log.error("abcd", e); throw new BOException("abcd", e); } // 吃掉了異常,外面不知道怎麼回事 try { .... } catch (Exception e) { } // 沒有正確記錄異常,日誌文件中記錄的信息太少不利於錯誤跟蹤 try { .... } catch (Exception e) { log.error("abcd" + e.getMessage); } try { .... } catch (Exception e) { log.error("abcd" + e); } try { .... } catch (Exception e) { log.error(e); } // 正確的使用 try { .... } catch (Exception e) { log.error("abcd", e); }
在咱們的項目IDCM中,是如何處理異常信息的呢?
RPC層處理異常信息:
@ResourceMapping("rackList") public DataResult<Rack> queryRackList(@RequestParams PagePara pagePara, @RequestParams Rack rack, @RequestParam(name = "operateType") String operateType) { DataResult<Rack> dataResult = new DataResult<Rack>(); try { // TODO 業務邏輯處理 } catch (Exception e) { logger.error("queryRackList err : ", e); if (e instanceof ServiceException) throw (ServiceException) e; else throw new ServiceException("查詢數據失敗"); } return dataResult; }
BoImpl層的異常信息:
@Override public DataResult<WorkOrderMain> queryOrderPagination(WorkOrderMain orderMain, PagePara pagePara) { DataResult<WorkOrderMain> dr = new DataResult<WorkOrderMain>(); try { // TODO 調用dao層的業務邏輯 } catch (Exception e) { logger.error(" WorkOrderLogisticsBoImpl_queryOrderPagination_error [orderMain={}]:", JSON.toJSON(orderMain).toString(), e); throw new ServiceException(ErrorCode.Query_Error, e); } return dr; }
4、日誌規範
日誌的做用: 記錄重要數據、操做以備往後覈對;記錄案發現場,方便往後定位問題。日誌的分類: 按日誌的用途能夠分爲系統異常日誌,應用相關日誌,用戶操做日誌等。不一樣類型的日誌分開記錄,系統異常日誌通常記錄在root logger下;如velocity相關的日誌也能夠分記到不一樣的日誌文件中,方便問題查找。記錄日誌INFO或DEGUB級別的應該先作log.isXXXenabled()判斷。(若是是應用日誌就打算記錄成INFO級別則不用這一條)業務代碼中,儘量少用DEBUG級別的日誌,容易混淆業務邏輯。在通用的底層代碼中,能夠適當用一些INFO和DEBUG級別的日誌。在調試代碼時能夠利用這些日誌信息,避免過於深刻的跟蹤。
異常日誌須要打印異常的詳細信息,如stackTrace等,方便問題查找。如今的框架會統一處理異常。本身捕獲異常處理後若是不繼續往外拋出,不要忘記記錄該異常。
import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; Log logger = LogFactory.getLog(getClass());//名字統一用logger Log log = LogFactory.getLog(getClass()); //不推薦
構造log格式:必須有的字段:時間(%d{yy-MM-dd HH:mm:ss})、日誌級別( %-5p)、類名(%c)、行號(%L) log內容(%m%n)
//正確 logger.error("xxxxx",e); //錯誤 logger.error("xxxxx"+e);
異常log,須要錯誤堆棧,異常對象,要放到第二個參數。
# 正確 $!{param} # 錯誤 $param
變量前面必定要有!。
#推薦
$!{param}
最好加上大括號(不強制),變量名先後加上{}。
5、Java編碼規範
重載方法不該該分開:當一個類有多個構造函數,或者多個同名成員方法時,這些函數應該寫在一塊兒,不該該被其餘成員分開。
命名:包名:包名所有用小寫字母,經過 . 將各級連在一塊兒。不該該使用下劃線。類名:類型的命名,採用以大寫字母開頭的大小寫字符間隔的方式(UpperCamelCase)。class命名通常使用名詞或名詞短語。interface的命名有時也可使用形容詞或形容詞短語。annotation沒有明確固定的規範。測試類的命名,應該以它所測試的類的名字爲開頭,並在最後加上Test結尾。例如:HashTest 、 HashIntegrationTest。(PS:常常見到寫成TestXXXX的類名,之後注意了)。方法名:方法命名,採用以小寫字母開頭的大小寫字符間隔的方式(lowerCamelCase)。方法命名通常使用動詞或者動詞短語。在JUnit的測試方法中,可使用下劃線,用來區分測試邏輯的名字,常用以下的結構:test<MethodUnderTest>_<state> 。例如:testPop_emptyStack 。測試方法也能夠用其餘方式進行命名。常量名:常量命名,所有使用大寫字符,詞與詞之間用下劃線隔開。(CONSTANCE_CASE),常量通常使用名詞或者名詞短語命名。PS:常量是一個靜態成員變量,但不是全部的靜態成員變量都是常量。在選擇使用常量命名規則給變量命名時,須要明確這個變量是不是常量。例如,若是這個變量的狀態能夠發生改變,那麼這個變量幾乎能夠確定不是常量。只是計劃不會發生改變的變量不足以成爲一個常量,可是咱們這裏把這種狀況也定義爲常量,因此也要大寫,下面是常量和很是量的例子:
// 常量 static final int NUMBER = 5; static final List<String> NAMES = Collections.unmodifiableList(Arrays.asList("ed", "ann")); static final ImmutableList<String> NAMES2 = ImmutableList.of("ed", "ann"); static final SomeMutableType[] EMPTY_ARRAY={}; enum SomeEnum{ENUM_CONSTANT} //不是常量 static String nonFinal = "non-final"; final String nonStatic = "non-static"; static final Set<String> mutableCollection = new HashSet<String>();//集合不是不可變的 //下面三種狀況雖然內部存放對象可能會改變,可是屬於計劃不會發生改變的變量,咱們也算作常量,因此也要大寫。 static final ImmutableSet<SomeMutableType> MUTABLE_ELEMENTS=ImmutableSet.of(mutable); static final Logger LOGGER=Logger.getLogger(MyClass.getName()); static final String[] NON_EMPTY_ARRAY = { "these", "can", "change" };
很是量的成員變量名:很是量的成員變量命名(包括靜態變量和非靜態變量),採用lowerCamelCase命名。通常使用名詞或名詞短語。參數名:參數命名採用lowerCamelCase命名。#應該避免使用一個字符做爲參數的命名方式#。特殊變量或參數名:DTO(Data Transfer Object):在變量、類名、參數等地方使用的時候,若是要用DTO結尾作名字,必須用大寫。例如:UserDTO.class、userDTO、userDTOs、userDTOList。DO(Data Object我們這邊常常用在DB操做):在變量、類名、參數等地方使用的時候,若是要用DO結尾作名字,必須用大寫。文件後綴:Service代表這個類是個服務類,裏面包含了給其餘類提同業務服務的方法。DAO這個類封裝了數據訪問方法。Impl這個類是一個實現類,而不是接口(一般是實現DAO和Service)。
6、代碼格式規範
if(a!=null)return true 須要改爲: if(a!=null){ return true; }
花括號通常用在if, else, for, do, 和 while等語句。甚至當它的實現爲空或者只有一句話時,也須要使用。
if(){ if(){ if(){ } } }
if語句的嵌套層數保證在3層之內。太多層嵌套,最後本身都看不懂。直觀感覺一下,加上代碼塊,就會很複雜,並且還會有else。尤爲是在代碼不斷增長需求的過程當中,要重構一些代碼,形成代碼if嵌套混亂。
類的文件代碼長度不要超過1000行。方法的長度不要超過150行,超出的考慮拆分一下。
public enum EnumExample { EXAMPLE_ONE, EXAMPLE_TWO; } public enum EnumExample { /** 例子一 */ EXAMPLE_ONE("1"), /** 例子二 */ EXAMPLE_TWO("2"); private EnumExample(String index) { } }
枚舉常常須要被外部引用,若是名字不能直接表達,須要加javadoc。枚舉名須要全大寫,單詞用「_」分割。
局部變量:局部變量不該該習慣性地放在語句塊的開始處聲明,而應該儘可能離它第一次使用的地方最近的地方聲明,以減少它們的使用範圍。局部變量應該在聲明的時候就進行初始化。若是不能在聲明時初始化,也應該儘快完成初始化。
switch必須有default:每一個switch語句中,都須要顯式聲明default標籤。即便沒有任何代碼也須要顯示聲明。
修飾符的順序:多個類和成員變量的修飾符,按Java Lauguage Specification中介紹的前後順序排序。具體是:
public protected private abstract static final transient volatile synchronized native strictfp
除註釋外的代碼,不容許出現中文。常常看到log裏面用中文,這樣不容許,還有用中文直接equals比較,也一樣不容許。
項目中禁止使用System.out.println()。若是有log需求,直接用log4j好了,能夠隨意指定輸出位置。
@Override public String toString() { //xxxxxx } }
@Override:@override 都應該使用。@override annotations只要是符合語法的,都應該使用。PS:若是沒寫Override intellij&eclipse默認都會有警告出現。
異常捕獲,不該該被忽略:通常狀況下,catch住的異常不該該被忽略,而是都須要作適當的處理。例如將錯誤日誌打印出來,或者若是認爲這種異常不會發生,則應該做爲斷言異常從新拋出。
try { return Integer.parseInt(response); }catch (NumberFormatException ok){ // 不是數字不要緊,繼續往下走就行啦 }
若是這個catch住的異常確實不須要任何處理,也應該經過註釋作出說明。
try { emptyStack.pop(); fail(); } catch (NoSuchMethodException expected) { }
在測試類裏,有時會針對方法是否會拋出指定的異常,這樣的異常是能夠被忽略的。可是這個異常一般須要命名爲: expected。
7、javadoc規範
對外的接口必定要有javadoc(強制),例如一些hsf服務什麼的接口必需要有。
第一種: /** * 我多行我開心 */ 第二種: /** 我單行我快樂 */
@從句:全部標準的@從句,應該按照以下的順序添加:@param、@return、@throws、@deprecated。而且這四種@從句,不該該出如今一個沒有描述的Javadoc塊中。
何處應該使用Javadoc:至少,Javadoc應該應用於全部的public類、public和protected的成員變量和方法。和少許例外的狀況。例外狀況以下:例外一:方法自己已經足夠說明的狀況。當方法自己很顯而易見時,能夠不須要javadoc。例如:getFoo。沒有必要加上javadoc說明「Returns the foo」。單元測試中的方法基本都能經過方法名,顯而易見地知道方法的做用。所以不須要增長javadoc。注意:有時候不該該引用此例外,來省略一些用戶須要知道的信息。例如:getCannicalName 。當大部分代碼閱讀者不知道canonical name是什麼意思時,不該該省略Javadoc,認爲只能寫/** Returns the canonical name. */例外二:重載方法:重載方法有時不須要再寫Javadoc。
8、二方庫版本控制規範
對外公開的api類二方庫版本控制規範 版本格式:主版本號.次版本號.修訂號,版本號遞增規則以下: 主版本號:當你作了不兼容的API 修改。 次版本號:當你作了向下兼容的功能性新增(新增接口、接口兼容性修改等)。 修訂號:當你作了向下兼容的問題修正或者實現的修改。 其餘版本版本編譯信息加到「主版本號.次版本號.修訂號」的後面,做爲延伸。 好處:版本號及其更新方式包含了相鄰版本間的底層代碼和修改內容的信息,經過版本號能夠知道如何升級,例如:次版本/修訂版本升級了,就能夠考慮升級,由於是向後兼容的。 版本控制規範: 標準的版本號XYZ格式,其中X、Y和Z爲非負的整數,X是主版本、Y是次版本號、而Z爲修訂號。每一個元素必須以數值來遞增。例如:1.9.1 -> 1.10.0 -> 1.11.0。 有版本號的二方庫發佈後,禁止改變該版本的內容。任何修改都必須增長新版本發行(snapshot除外)。 主版本號爲零(0.yz)的軟件處於開發初始階段,一切均可能隨時被改變。這樣的公共API 不該該被視爲穩定版。 1.0.0 的版本號用於界定公共API 的造成。這一版本以後全部的版本號更新都基於公共API 及其修改內容。 修訂號Z(xyZ | x > 0)「必須」在只作了向下兼容的修正時才遞增。這裏的修正指的是針對不正確結果而進行的內部修改(例如:修bug)或者在內部程序有大量新功能或改進被加入時遞增(咱們業務需求常常乾的)。在任何公共API的功能被標記爲棄用時也「必須」遞增(例如:廢棄了某個接口)。 次版本號Y(xYz | x > 0)「必須」在有向下兼容的新功能出現時遞增。(例如:新增接口/接口參數從int變爲Integer相似),但每當次版本號(Y)遞增時,修訂號(Z)「必須」歸零。 主版本號X(Xyz | X > 0)「必須」在有任何不兼容的修改被加入公共API時遞增。其中「能夠」包括次版本號(Y)及修訂級別(Z)的改變。每當主版本號遞增時,次版本號和修訂號「必須」歸零。 先行版本(例如SNAPSHOT版本、alpha等)能夠標註在修訂版(Z)以後,先加上一個鏈接號()再加上一連串以句點分隔的標識符號來修飾。標識符號「必須」由ASCII碼的英數字和鏈接號[0-9A-Za-z]組成,且「禁止」留白。數字型的標識符號「禁止」在前方補零。被標上先行版本號則表示這個版本並不是穩定並且可能沒法達到兼容的需求。範例:1.0.0-snapshot、1.0.0-alpha、1.0.0-alpha.一、 1.0.0-0.3.七、1.0.0-x.7.z.92。 咱們的實踐方案 根據上面的規範,咱們的對外二方庫,能夠遵循上面的方式開發,不能使用SNAPSHOT版本,對內的使用或者不適用SNAPSHOT均可以。 若是是對外的api(例如:market.open.share),有對應的open.client這種sdk,open.share和open.client必須聯動升級,例如:open.share 主次版本修改open.client也必須跟着修改,而且版本一致。 外部使用,market舉例,針對有open.client這種sdk的對外api二方庫,使用api的時候,直接依賴open.client就能夠了,不須要手動指定open.share,緣由見上一條。 對外公佈的二方庫裏面增長文件(README.md) 記錄每一個主版本和次版本號的升級日誌。 對外open的二方庫只能依賴其餘open的二方庫或者三方庫,不能依賴本身的非open的二方庫。 PS: 對內的二方庫使用SNAPSHOT版本,要大寫
9、三方庫的使用
import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; List<String> keys = Lists.newArrayListWithCapacity(infos.size()); Map<K, V> resultMap = Maps.newHashMapWithExpectedSize(tmpList.size()); Set<String> sets=Sets.newHashSet();
建立集合&Map:推薦使用guava的靜態方法,建立集合。
字符串操做:優先使用:StringUtils(org.apache.commons.lang3.StringUtils)經常使用方法:isBlank、isNotBlank。
數組操做:優先使用:ArrayUtils(org.apache.commons.lang3.ArrayUtils)。經常使用方法:isEmpty、isNotEmpty、toString(final Object array)。
集合操做:優先使用:CollectionUtils(org.apache.commons.collections4.CollectionUtils)經常使用方法:isEmpty、isNotEmpty、size。
Map操做:優先使用:MapUtils(org.apache.commons.collections4.MapUtils)經常使用方法:isEmpty、isNotEmpty。
除上面之外還有NumberUtils、DateFormatUtils、DateUtils等優先使用org.apache.commons.lang3這個包下的,不要使用org.apache.commons.lang包下面的。緣由是commons.lang這個包是從jdk1.2開始支持的因此不少1.5/1.6的特性是不支持的,例如:泛型。
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; public class User { private String name; private User(String name) { this.name = name; } @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } public static void main(String[] args) { System.out.println(new User("testname")); } } 輸出:User[name=testname]
重載toString方法:優先使用:ToStringBuilder(org.apache.commons.lang3.builder.ToStringBuilder)。
相關第三方庫依賴以下:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-collections4</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency>
10、消滅警告規範
消滅警告:警告原本就是可能有問題的代碼,仍是有可能會影響正常功能。或者至少去掉會讓代碼更簡潔,PS:警告eclipse左側會顯示屎黃色的小標。
用findbugs掃一下:好處:幫助咱們發現不少問題和優化意見(貌似最多的就是空指針)。首先裝個findbugs插件,裝好了右鍵執行,完成後在Bug Explorer視圖裏面看結果。
11、codereview規範
codereview:發佈代碼前在aone指定codereview的人,進行代碼審查。可是代碼審查並非說只找一我的,這能夠是團隊多人一塊兒作。進行reivew的人也不必定必須是老人,有時咱們可能以爲「經驗比較淺,不能對別人codereivew」,其實並不必定,俗話說:三個臭皮匠,頂個諸葛亮。即便是經驗欠缺,多我的review,也能完成高質量的代碼。
怎麼作codereivew呢?
12、