?
?
?
?
?
?
1、簡介
?
統一規范標準將有助于提高行業編碼規范化水平,幫助行業人員提高開發質量和效率、大大降低代碼維護成本
?
2017年年初,首次公開了《阿里巴巴Java開發手冊》,自從第一個版本起,倍受業界關注。為了讓開發者更加方便、快速的將規范推動并實行起來,阿里巴巴基于手冊內容,研發了一套自動化的IDE檢測插件(IDEA、Eclipse), 該插件在掃描代碼后,將不符合《手冊》的代碼按Blocker/Critical/Major三個等級顯示在下方,甚至在IDEA上,還基于Inspection機制提供了實時檢測功能,編寫代碼的同時也能快速發現問題所在。對于歷史代碼,部分規則實現了批量一鍵修復的功能,提升代碼質量,提高團隊研發效能。
?
接下來我們先來學習學習阿里編程規范再基于Intellij來學習阿里插件的安裝及使用:
?
?
2、命名:
?
-
代碼中的命名均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束。
反例:_name / name / name/name/namename/name/name name / name_ / name name/name/?name / name -
代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。
說明:正確的英文拼寫和語法可以讓閱讀者易于理解,避免歧義。注意,純拼音命名方式更要避免采用。
正例:renminbi / alibaba / taobao / youku / hangzhou 等國際通用的名稱,可視同英文。
反例:DaZhePromotion [打折] / getPingfenByName() [評分] / int 某變量 = 3 -
在 long 或者 Long 賦值時,數值后使用大寫的 L,不能是小寫的 l,小寫容易跟數字 1 混淆,造成誤解。
-
杜絕完全不規范的縮寫,避免望文不知義。
反例:AbstractClass“縮寫”命名成 AbsClass;condition“縮寫”命名成 condi,此類隨意縮寫嚴重降低了代碼的可閱讀性。 -
為了達到代碼自解釋的目標,任何自定義編程元素在命名時,使用盡量完整的單詞組合來表達其意。
正例:在 JDK 中,表達原子更新的類名為:AtomicReferenceFieldUpdater。
反例:int a 的隨意命名方式。 -
常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。
-
不允許硬編碼,即不允許任何魔法值( 即未經定義的常量 ) 直接出現在代碼中。
正例:PAGE_SIZE / THREAD_POOL_SIZE
反例: String key =” Id # taobao _”+ tradeId; -
在常量與變量的命名時,表示類型的名詞放在詞尾,以提升辨識度。
正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT
反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD -
不要使用一個常量類維護所有常量,要按常量功能進行歸類,分開維護。
說明:大而全的常量類,雜亂無章,使用查找功能才能定位到修改的常量,不利于理解和維護。
正例:緩存相關常量放在類 CacheConsts 下;系統配置相關常量放在類 ConfigConsts 下。 -
如果變量值僅在一個固定范圍內變化用 enum 類型來定義。
-
類型與中括號緊挨相連來表示數組。
正例:定義整形數組 int[] arrayDemo;
反例:在 main 參數中,使用 String args[]來定義。 -
方法名、參數名、成員變量、局部變量都統一使用lowerCamelCase 風格,必須遵從駝峰形式。
正例: localValue / getHttpMessage() / inputUserId -
類名使用UpperCamelCase 風格,必須遵從駝峰形式,但以下情形例外: ( 領域模型的相關命名 )DO / BO / DTO / VO 等。
正例: MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例: macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion -
如果模塊、接口、類、方法使用了設計模式,在命名時需體現出具體模式。
說明:將設計模式體現在名字中,有利于閱讀者快速理解架構設計理念。
正例: public class OrderFactory;
public class LoginProxy;
public class ResourceObserver; -
接口類中的方法和屬性不要加任何修飾符號(public 也不要加),保持代碼的簡潔性,并加上有效的 Javadoc 注釋。盡量不要在接口里定義變量,如果一定要定義變量,肯定是與接口方法相關,并且是整個應用的基礎常量。
-
包名統一使用小寫,點分隔符之間有且僅有一個自然語義的英語單詞。包名統一使用單數形式,但是類名如果有復數含義,類名可以使用復數形式。
正例:應用工具類包名為 com.alibaba.ai.util、類名為 MessageUtils(此規則參考 spring 的框架結構) -
抽象類命名使用 Abstract 或 Base 開頭 ; 異常類命名使用 Exception 結尾 ; 測試類命名以它要測試的類的名稱開始,以 Test 結尾。枚舉類名建議帶上 Enum 后綴,枚舉成員名稱需要全大寫,單詞間用下劃線隔開。
-
避免在子父類的成員變量之間、或者不同代碼塊的局部變量之間采用完全相同的命名,使可讀性降低。
說明:子類、父類成員變量名相同,即使是 public 類型的變量也是能夠通過編譯,而局部變量在同一方法內的不同代碼塊中同名也是合法的,但是要避免使用。對于非 setter/getter 的參數名稱也要避免與成員變量名稱相同。 -
POJO 類中布爾類型的變量,都不要加 is ,否則部分框架解析會引起序列化錯誤。
-
Service / DAO 層方法命名規約
1)獲取單個對象的方法用 get 做前綴。
2)獲取多個對象的方法用 list 做前綴(習慣:getXXXList)。
3)獲取統計值的方法用 count 做前綴。
4)插入的方法用 save( 推薦 ) 或 insert 做前綴。
5)刪除的方法用 remove( 推薦 ) 或 delete 做前綴。
6)修改的方法用 update 做前綴(或modify)。 -
領域模型命名規約
1)數據對象: xxxDO , xxx 即為數據表名。
2)數據傳輸對象: xxxDTO , xxx 為業務領域相關的名稱。
3)展示對象: xxxVO , xxx 一般為網頁名稱。
4)POJO 是 DO / DTO / BO / VO 的統稱,禁止命名成 xxxPOJO 。
?
3、格式規約
?
-
如果是大括號內為空,則簡潔地寫成{}即可,大括號中間無需換行和空格;如果是非空代碼塊則:
1) 左大括號前不換行。
2) 左大括號后換行。
3) 右大括號前換行。
4) 右大括號后還有 else 等代碼則不換行;表示終止的右大括號后必須換行。 -
左小括號和字符之間不出現空格;同樣,右小括號和字符之間也不出現空格;而左大括號前需要空格。
-
if/for/while/switch/do 等保留字與括號之間都必須加空格。
-
任何二目、三目運算符的左右兩邊都需要加一個空格。
說明:運算符包括賦值運算符=、邏輯運算符&&、加減乘除符號等。 -
采用 4 個空格縮進,禁止使用 tab 字符。
-
注釋的雙斜線與注釋內容之間有且僅有一個空格。
-
單行太長需換行
-
方法體內的執行語句組、變量的定義語句組、不同的業務邏輯之間或者不同的語義之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。
?
4、注釋規約
?
-
類、類屬性、類方法的注釋必須使用 Javadoc 規范,使用/*內容/格式,不得使用 //xxx 方式。
-
所有的抽象方法(包括接口中的方法)必須要用 Javadoc 注釋、除了返回值、參數、 異常說明外,還必須指出該方法做什么事情,實現什么功能。對子類的實現要求,或者調用注意事項,需要一并說明。
-
方法內部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法內部多行注釋使用/* */注釋,注意與代碼對齊。
-
所有的枚舉類型字段必須要有注釋,說明每個數據項的用途。
-
代碼修改的同時,注釋也要進行相應的修改,尤其是參數、返回值、異常、核心邏輯等的修改。
-
注釋掉的代碼盡量要配合說明,而不是簡單的注釋掉。說明:代碼被注釋掉有兩種可能性:
1)后續會恢復此段代碼邏輯。如果沒有備注信息,難以知曉注釋動機。
2)永久不用。建議直接刪掉(代碼倉庫保存了歷史代碼)。 -
特殊注釋標記,請注明標記人與標記時間。注意及時處理這些標記,通過標記掃描, 經常清理此類標記。線上故障有時候就是來源于這些標記處的代碼。
1)待辦事宜(TODO):( 標記人,標記時間,[預計處理時間]) 表示需要實現,但目前還未實現的功能。這實際上是一個 Javadoc 的標簽,目前的 Javadoc還沒有實現,但已經被廣泛使用。只能應用于類,接口和方法(因為它是一個 Javadoc 標簽)。
2)錯誤,不能工作(FIXME):(標記人,標記時間,[預計處理時間])在注釋中用 FIXME 標記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。
?
5、OOP規約
?
-
避免通過類的對象引用訪問此類的靜態變量或靜態方法,無謂增加編譯器解析成本,直接用類名來訪問即可。
-
所有的覆寫方法,必須加@ Override 注解。
-
對外暴露的接口簽名,原則上不允許修改方法簽名,避免對接口調用方產生影響。
-
接口過時必須加@Deprecated 注解,并清晰地說明采用的新接口或者新服務是什么。
-
在使用正則表達式時,利用好其預編譯功能,可以有效加快正則匹配速度。 不要在方法體內定義:
Pattern pattern = Pattern.compile(規則);
-
獲取當前毫秒數 System.currentTimeMillis(); 而不是 new Date().getTime();
說明:如果想獲取更加精確的納秒級時間值,用 System.nanoTime()。在 JDK8 中,針對統計時間等場景,推薦使用Instant 類。 -
對于“明確停止使用的代碼和配置”,如方法、變量、類、配置文件、動態配置屬性等要堅決從程序中清理出去,避免造成過多垃圾。
-
Object 的 equals 方法容易拋空指針異常,應使用常量或確定有值的對象來調用equals。
正例: ” test ” .equals(object);
反例: object.equals( ” test ” ); -
所有的相同類型的包裝類對象之間值的比較,全部使用 equals 方法比較。(注意空指針)
說明:對于 Integer var = ?在-128 至 127 之間的賦值, Integer 對象是在IntegerCache . cache 產生,會復用已有對象,這個區間內的 Integer 值可以直接使用==進行判斷,但是這個區間之外的所有數據,都會在堆上產生,并不會復用已有對象,這是一個大坑,推薦使用 equals 方法進行判斷。 -
關于基本數據類型與包裝數據類型的使用標準如下:
1)所有的 POJO 類屬性必須使用包裝數據類型。
2)RPC 方法的返回值和參數必須使用包裝數據類型。
3)所有的局部變量【推薦】使用基本數據類型。 -
序列化類新增屬性時,請不要修改 serialVersionUID 字段,避免反序列失敗 ; 如果完全不兼容升級,避免反序列化混亂,那么請修改 serialVersionUID 值。
-
構造方法里面禁止加入任何業務邏輯,如果有初始化邏輯,請放在 init 方法中。
-
使用索引訪問用 String 的 split 方法得到的數組時,需做最后一個分隔符后有無內容的檢查,否則會有拋 IndexOutOfBoundsException 的風險。說明:
String str = “a,b,c,,”; String[] ary = str.split(“,”); //預期大于 3,結果是 3 System.out.println(ary.length);
-
當一個類有多個構造方法,或者多個同名方法,這些方法應該按順序放置在一起,便于閱讀。
-
類內方法定義順序依次是:公有方法或保護方法 > 私有方法 > getter / setter方法。
-
final 可提高程序響應效率,聲明成 final 的情況:
1)不需要重新賦值的變量,包括類屬性、局部變量。
2)對象參數前加 final ,表示不允許修改引用的指向。
3)類方法確定不允許被重寫。
?
6、集合處理
?
-
關于 hashCode 和 equals 的處理,遵循如下規則:
1)只要重寫 equals ,就必須重寫 hashCode 。
2)因為 Set 存儲的是不重復的對象,依據 hashCode 和 equals 進行判斷,所以 Set 存儲的對象必須重寫這兩個方法。
3)如果自定義對象做為 Map 的鍵,那么必須重寫 hashCode 和 equals 。 -
不要在 foreach 循環里進行元素的 remove / add 操作。 remove 元素請使用 Iterator方式,如果并發操作,需要對 Iterator 對象加鎖。
反例:List<String> a = new ArrayList<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();} }
-
集合初始化時,盡量指定集合初始值大小。
說明: ArrayList 盡量使用 ArrayList(int initialCapacity) 初始化。 -
使用 entrySet 遍歷 Map 類集合 KV,而不是 keySet 方式進行遍歷。
說明:keySet 其實是遍歷了 2 次,一次是轉為 Iterator 對象,另一次是從 hashMap 中取出 key 所對應的 value。而 entrySet 只是遍歷了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。Map<String, String> map = new HashMap<String, String>(); map.put(“1”, “@@”); map.put(“2”, “#”);/** * JDK8推薦使用 */ map.forEach((K, V) -> {System.out.println(“Key : ” + K + “ Value : ” + V); });/** * foreach推薦使用 */ for (Map.Entry<String, String> entry : map.entrySet()) {System.out.println(“Key : ” + entry.getKey());System.out.println(“Value : ” + entry.getValue()); }/** * 不推薦使用 */ for (String key : map.keySet()) {System.out.println(“Key : ” + key);System.out.println(“Value : ” + map.get(key)); }
-
Map 類集合 K/V 能不能存儲 null 值的情況,如下表格:
?
集合類 | Key | Value | Super | 說明 |
---|---|---|---|---|
Hashtable | 不允許為 null | 不允許為 null | Dictionary | 線程安全 |
ConcurrentHashMap | 不允許為 null | 不允許為 null | AbstractMap | 分段鎖技術 |
TreeMap | 不允許為 null | 允許為 null | AbstractMap | 線程不安全 |
HashMap | 允許為 null | 允許為 null | AbstractMap | 線程不安全 |
7、控制語句
?
-
在每一個switch 塊內,每個 case 要么通過 break/return 等來終止,要么注釋說明程序將繼續執行到哪一個 case 為止;在每一個 switch 塊內,都必須包含一個 default 語句并且放在最后,即使它什么代碼也沒有。
-
在 if/else/for/while/do 語句中必須使用大括號,即使只有一行代碼。
-
盡量少用 else, if-else 的方式可以改寫成:
if(condition){…return obj; } // 接著寫 else 的業務邏輯代碼;
說明:如果非得使用if()…else if()…else…方式表達邏輯,【強制】請勿超過3層,超過請使用狀態設計模式或者衛語句。
衛語句示例:public void today() { if (isBusy()) { System.out.println(“change time.”); return; } if (isFree()) { System.out.println(“go to travel.”); return; } System.out.println(“stay at home to learn Alibaba Java Coding Guidelines.”); return; }
-
除常用方法(如 getXxx/isXxx)外,不要在條件判斷中執行其它復雜的語句,將復雜邏輯判斷的結果賦值給一個有意義的布爾變量名,以提高可讀性。
正例:boolean existed = (file.open(fileName, “w”) != null) && (…) || (…); if (existed) {… }
反例:
if ((file.open(fileName, “w”) != null) && (…) || (…)) { … }
-
方法中需要進行參數校驗的場景:
1)調用頻次低的方法。
2)執行時間開銷很大的方法,參數校驗時間幾乎可以忽略不計,但如果因為參數錯誤導致中間執行回退,或者錯誤,那得不償失。
3)需要極高穩定性和可用性的方法。
4)對外提供的開放接口,不管是RPC/API/HTTP接口。
5)敏感權限入口。 -
方法中不需要參數校驗的場景:
1)極有可能被循環調用的方法,不建議對參數進行校驗。但在方法說明里必須注明外部參數檢查。
2)底層的方法調用頻度都比較高,一般不校驗。一般 DAO 層與 Service 層都在同一個應用中,部署在同一 臺服務器中,所以 DAO 的參數校驗,可以省略。
3)被聲明成private只會被自己代碼所調用的方法,如果能夠確定調用方法的代碼傳入參數已經做過檢查或者肯定不會有問題,此時可以不校驗參數。
?
8、異常
?
-
異常不要用來做流程控制,條件控制,因為異常的處理效率比條件分支低。
-
對大段代碼進行 try-catch,這是不負責任的表現。catch 時請分清穩定代碼和非穩 定代碼,穩定代碼指的是無論如何不會出錯的代碼。對于非穩定代碼的 catch 盡可能進行區分異常類型,再做對應的異常處理。
-
捕獲異常是為了處理它,不要捕獲了卻什么都不處理而拋棄之,如果不想處理它,請將該異常拋給它的調用者。最外層的業務使用者,必須處理異常,將其轉化為用戶可以理解的內容。
-
有 try 塊放到了事務代碼中,catch 異常后,如果需要回滾事務,一定要注意手動回滾事務。
-
不能在 finally 塊中使用 return,finally 塊中的 return 返回后方法結束執行,不會再執行 try 塊中的 return 語句。
-
方法的返回值可以為 null,不強制返回空集合,或者空對象等,必須添加注釋充分說明什么情況下會返回 null 值。調用方需要進行 null 判斷防止 NPE 問題。
-
防止 NPE,是程序員的基本修養,注意 NPE 產生的場景:
1)返回類型為包裝數據類型,有可能是null,返回int值時注意判空。
反例:public int fun(){ return Integer 對象}; 如果為 null,自動解箱拋 NPE。
2)數據庫的查詢結果可能為null。
3)集合里的元素即使isNotEmpty,取出的數據元素也可能為null。
4)遠程調用返回對象,一律要求進行NPE判斷。
5)對于Session中獲取的數據,建議NPE檢查,避免空指針。
6)級聯調用obj.getA().getB().getC();一連串調用,易產生NPE。 -
在代碼中使用“拋異常”還是“返回錯誤碼”,對于公司外的 http/api 開放接口必須使用“錯誤碼”;而應用內部推薦異常拋出;跨應用間 RPC 調用優先考慮使用 Result 方式,封裝 isSuccess、“錯誤碼”、“錯誤簡短信息”。
說明:關于 RPC 方法返回方式使用 Result 方式的理由:
1)使用拋異常返回方式,調用方如果沒有捕獲到就會產生運行時錯誤。
2)如果不加棧信息,只是new自定義異常,加入自己的理解的error message,對于調用端解決問題的幫助不會太多。如果加了棧信息,在頻繁調用出錯的情況下,數據序列化和傳輸的性能損耗也是問題。 -
避免出現重復的代碼(Don’t Repeat Yourself),即DRY原則。
?
9、日志
?
-
應用中不可直接使用日志系統(Log4j、Logback)中的 API,而應依賴使用日志框架SLF4J 中的 API,使用門面模式的日志框架,有利于維護和各個類的日志處理方式統一。
import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static final Logger logger = LoggerFactory.getLogger(Abc.class);
-
日志文件推薦至少保存 15 天,因為有些異常具備以“周”為頻次發生的特點。
-
應用中的擴展日志(如打點、臨時監控、訪問日志等)命名方式: appName_logType_logName.log。其中:
logType:日志類型,推薦分類有 stats/desc/monitor/visit 等;
logName:日志描述。這種命名的好處:通過文件名就可知道日志文件屬于什么應用、什么類型、什么目的、也有利于歸類查找。
正例:mppserver 應用中單獨監控時區轉換異常,如: mppserver_monitor_timeZoneConvert.log
說明:推薦對日志進行分類,錯誤日志和業務日志盡量分開存放,便于開發人員查看,也便于通過日志對系統進行及時監控。 -
對 trace/debug/info 級別的日志輸出,必須使用條件輸出形式或者使用占位符的方式。
說明:logger.debug(“Processing trade 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: {} symbol : {} “, id, symbol);
-
避免重復打印日志,浪費磁盤空間,務必在 log4j.xml 中設置 additivity=false。
正例: -
可以使用warn 日志級別來記錄用戶輸入參數錯誤的情況,避免用戶投訴時,無所適從。注意日志輸出的級別,error 級別只記錄系統邏輯出錯、異常等重要的錯誤信息。如非必要,請不要在此場景打出 error 級別。
-
謹慎地記錄日志。生產環境禁止輸出 debug 日志;有選擇地輸出 info 日志;如果使 用 warn 來記錄剛上線時的業務行為信息,一定要注意日志輸出量的問題,避免把服務器磁盤撐爆,并記得及時刪除這些觀察日志。
說明:大量地輸出無效日志,不利于系統性能提升,也不利于快速定位錯誤點。記錄日志時請思考:這些日志真的有人看嗎?看到這條日志你能做什么?能不能給問題排查帶來好處?
?
10、單元測試
?
-
好的單元測試必須遵守 AIR 原則。
說明:單元測試在線上運行時,感覺像空氣(AIR)一樣并不存在,但在測試質量的保障上,卻是非常關鍵的。好的單元測試宏觀上來說,具有自動化、獨立性、可重復執行的特點。
A:Automatic(自動化)
I:Independent(獨立性)
R:Repeatable(可重復) -
單元測試應該是全自動執行的,并且非交互式的。測試框架通常是定期執行的,執行過程必須完全自動化才有意義。輸出結果需要人工檢查的測試不是一個好的單元測試。單元測試中不準使用 System.out 來進行人肉驗證,必須使用 assert 來驗證。
-
保持單元測試的獨立性。為了保證單元測試穩定可靠且便于維護,單元測試用例之間決不能互相調用,也不能依賴執行的先后次序。
-
對于單元測試,要保證測試粒度足夠小,有助于精確定位問題。單測粒度至多是類級別,一般是方法級別。
說明:只有測試粒度小才能在出錯時盡快定位到出錯位置。單測不負責檢查跨類或者跨系統的交互邏輯,那是集成測試的領域。 -
核心業務、核心應用、核心模塊的增量代碼確保單元測試通過。
說明:新增代碼及時補充單元測試,如果新增代碼影響了原有單元測試,請及時修正。 -
單元測試的基本目標:語句覆蓋率達到 70%;核心模塊的語句覆蓋率和分支覆蓋率都要達到 100%
說明:在工程規約的應用分層中提到的 DAO 層、Manager 層、可重用度高的 Service都應該進行單元測試。 -
編寫單元測試代碼遵守 BCDE 原則,以保證被測試模塊的交付質量。
B:Border,邊界值測試,包括循環邊界、特殊取值、特殊時間點、數據順序等。
C:Correct,正確的輸入,并得到預期的結果。
D:Design,與設計文檔相結合,來編寫單元測試。
E:Error,強制錯誤信息輸入(如:非法數據、異常流程、非業務允許輸入等),并得到預期的結果。 -
和數據庫相關的單元測試,可以設定自動回滾機制,不給數據庫造成臟數據。或者對單元測試產生的數據有明確的前后綴標識。
-
在設計評審階段,開發人員需要和測試人員一起確定單元測試范圍,單元測試最好覆蓋所有測試用例(UC)。
-
單元測試作為一種質量保障手段,不建議項目發布后補充單元測試用例,建議在項目提測前完成單元測試。
?
11、工程規約
?
圖中默認上層依賴于下層,箭頭關系表示可直接依賴,如:開放接口層可以依賴于Web 層,也可以直接依賴于 Service 層,依此類推。
?
-
開放接口層:可直接封裝 Service接口暴露成 RPC 接口、通過 Web 封裝成 http 接口、網關控制層等。
-
終端顯示層:各個端的模板渲染并執行顯示層。當前主要是 velocity 渲染、JS 渲染、JSP 渲 染、移動端展示層等。
-
Web 層:主要是對訪問控制進行轉發,各類基本參數校驗,或者不復用的業務簡單處理等。(Controller)
-
Service 層:相對具體的業務邏輯服務層。
-
Manager 層:通用業務處理層,它有如下特征:
1)對第三方平臺封裝的層,預處理返回結果及轉化異常信息;
2)對Service層通用能力的下沉,如緩存方案、中間件通用處理;
3)與DAO層交互,對DAO的業務通用能力的封裝。 -
DAO 層:數據訪問層,與底層 MySQL、Oracle、Hbase 進行數據交互。
-
外部接口或第三方平臺:包括其它部門 RPC 開放接口,基礎平臺,其它公司的 HTTP 接口。
-
(分層異常處理規約)在 DAO 層,產生的異常類型有很多,無法用細粒度的異常進 行catch,使用catch(Exception e)方式,并throw new DAOException(e),不需要打印日志,因為日志在 Manager/Service 層一定需要捕獲并打到日志文件中去,如果同臺服務器再打日志,浪費性能和存儲。在 Service 層出現異常時,必須記錄出錯日志到磁盤,盡可能帶上參數信息,相當于保護案發現場。如果 Manager 層與 Service 同機部署,日志方式與 DAO 層處理一致,如果是單獨部署,則采用與 Service 一致的處理方式。Web 層絕不應該繼續往上拋異常,因為已經處于頂層,如果意識到這個異常將導致頁面無法正常渲染,那么就應該直接跳轉到友好錯誤頁面,加上用戶容易理解的錯誤提示信息。開放接口層要將異常處理成錯誤碼和錯誤信息方式返回。
-
分層領域模型規約:
1)DO(Data Object):與數據庫表結構一一對應,通過 DAO 層向上傳輸數據源對象。(Entity)
2)DTO(Data Transfer Object):數據傳輸對象,Service 和 Manager 向外傳輸的對象。
3) BO(Business Object):業務對象。可以由 Service 層輸出的封裝業務邏輯的對象。
4)QUERY:數據查詢對象,各層接收上層的查詢請求。注:超過 2 個參數的查詢封裝,禁止使用 Map 類來傳輸。
5)VO(View Object):顯示層對象,通常是 Web 向模板渲染引擎層傳輸的對象。
---------------------
作者:梁云亮
來源:CSDN
原文:https://blog.csdn.net/lianghecai52171314/article/details/102711958
版權聲明:本文為作者原創文章,轉載請附上博文鏈接!
內容解析By:CSDN,CNBLOG博客文章一鍵轉載插件