文章轉(zhuǎn)載自:http://kiral.javaeye.com/blog/606348?page=2#comments 原文還包括精彩的討論。
馬上要告別研發(fā)了,所以寫一些自己積累的經(jīng)驗,用來紀(jì)念4年的似水流年,本篇為第一張,用來介紹自己是如何寫Service的,當(dāng)然我總結(jié)的不一定合理,大家一起討論下。
筆者認(rèn)為,Service及服務(wù)層,服務(wù)可以分為功能服務(wù)和業(yè)務(wù)服務(wù),功能服務(wù)不易改變,業(yè)務(wù)服務(wù)易改變。所以功能服務(wù)添加得多,修改的少,那么我們可以考慮不使用接口。而業(yè)務(wù)服務(wù),修改和更新都很頻繁,所以應(yīng)該提取接口,用不同的實現(xiàn)來屏蔽業(yè)務(wù)邏輯。
1:使用斷言控制輸入。
使用斷言來判斷有效的輸入,這樣能夠避免異常的擴散,迅速定位錯誤和減少BUG出現(xiàn)的幾率。
如:
- import org.springframework.util.Assert;
- private boolean addAttachment(Attachment att) {
- Assert.notNull(att, "att對象不能為空");
- }
要學(xué)會靈活運用斷言,不僅僅是用來斷言來判斷方法的輸入?yún)?shù)是否正確,還可以判斷業(yè)務(wù)邏輯,每次方法調(diào)用的輸入輸出,至于何時使用需要自己根據(jù)方法自我判斷。
2:只拋出RumtimeException
作為service層,自己不清楚調(diào)用方到底是誰,也不知道調(diào)用方如何使用自己的接口,那么自己寫出的接口最好是拋出RumtimeException,這樣調(diào)用方能夠處理這個異常或者覺得處理這個異常有必要的話,就進(jìn)行處理。如果使用Exception就得強制那些處理不了的調(diào)用方繼續(xù)向外拋出。拋出RumtimeException的時候需要在注釋里申明我拋出了該異常。
- throw new RuntimeException("工作流初始化失??!");
3:在Service層做事務(wù)處理
大家都知道Service層一般是用來組合DAO,所以經(jīng)常出現(xiàn)需要事務(wù)處理的地方,筆者建議盡量在service層做事務(wù)處理。
因為一般業(yè)務(wù)邏輯都屏蔽在service層。筆者習(xí)慣使用Spring的手動事務(wù)。
- new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
- protected void doInTransactionWithoutResult(TransactionStatus status) {
- //調(diào)用DAO按照ID刪除部門
- }
- });
4:Service接口的異常處理
對于程序異常,service能夠處理的自己處理(將異常封裝成自己的異常,再向外拋出也算一種處理),不能處理的繼續(xù)向外拋出。
對于業(yè)務(wù)異常,以前開發(fā)的時候都會向外拋出一個用戶友好的運行時異常,這種異常信息是能夠直接展現(xiàn)給用戶的,如“您添加的用戶名已經(jīng)存在!”,但是現(xiàn)在考慮到國際化,所以覺得Service的接口應(yīng)該拋出錯誤代碼,定義一個友好錯誤代碼運行時異常,在程序出現(xiàn)條件錯誤的時候拋出錯誤代碼。錯誤代碼可以定義一個枚舉類來實現(xiàn)。
- /**
- * 錯誤代碼定義
- *
- * @author fangtengfei
- * @date 2010-3-3
- */
- public enum ErrorCode {
- /**
- * 用戶不能重復(fù)
- */
- User_Not_Repeat,
- /**
- * 用戶名太長
- */
- User_Name_Too_Long
- }
- 在Service里拋出:throw new FriendlyCodeRuntimeException(ErrorCode.User_Not_Repeat.toString());
5:必須記錄日志
大家都知道,記錄日志的目的,主要是當(dāng)程序運行在不同的環(huán)境下,使用日志來監(jiān)控程序的運行,有些異??赡軙囟ǖ沫h(huán)境發(fā)生,而這種環(huán)境不容易被重現(xiàn),所以此時唯一能定位問題的途徑就只有日志。
Service層會被各種調(diào)用方使用,特別是對外提供Service,環(huán)境更會前差萬別,如何迅速并有效的定位錯誤變得尤其重要,所以必須記錄有效的日志。
- logger.error("更新文檔出現(xiàn)出錯", e);
6:寫有效的注釋
之所以說寫有效的注釋,是因為有時候,有些方法真的不需要寫注釋,如addUser,就不要在寫注釋“添加用戶”這樣的注釋。關(guān)鍵是寫有效的注釋,注釋的作用在于,調(diào)用方只看注釋而不看代碼就能知道如何使用接口,注釋應(yīng)該包括:輸入?yún)?shù)的注釋,輸出參數(shù)的注釋和異常的注釋。特別是List<Map>,Sting[]這樣的參數(shù)要嚴(yán)格說明,筆者認(rèn)為Service作為一個核心層,注釋必須非常詳細(xì)。另外直觀的方法名也能起到注釋的作用。
- /**
- * 批量添加文檔的附件
- *
- * @param att 附件對象,附件名長度為20,附件大小為10M
- * @throws FriendlyCodeRuntimeException
- */
- private void addAttachment(Attachment... attachment)