1. 程式人生 > >異常拋出與捕獲的思考

異常拋出與捕獲的思考

方法返回值 title width 自定義 con 相對 http 過多 tro

異常處理的思考

在java中異常分為兩類。一、檢查性異常。二、非檢查性異常(運行時異常)

技術分享圖片

二者的區別:檢查性異常需要顯式try-catch或者throw。運行時異常可以不用捕獲。

對於檢查性異常由於必須捕獲,所有並不需要太多的討論(在設計異常的時候需要考慮)。主要討論運行時異常的拋出與捕獲。

運行時異常的正確捕獲和拋出,能夠讓系統代碼更加簡潔與rd只需要關系正常業務,對於不正常業務則交由異常處理。

現在存在的困擾:

1、每調用一個其他方法,都需要考慮與分析這個方法是不是存在異常,影響正常業務流程。

2、在業務調用鏈中,每個方法都存在try-catch塊,代碼結構沈余,不利於維護,每個方法都捕獲與打印異常,造成日誌或者錯誤信息重復,不利於精確定位問題。

3、在業務方法調用鏈中,一個業務的結果對象會作為方法的返回值,在調用連中層層傳遞,並需要構造正常結果與異常結果。返回值有時候並不能快速或者正確得終止異常流程,需要很多的return語句塊。

在java web結構中整體的一個設想的是:

技術分享圖片

1、在基礎方法(非業務方法)中進行異常的捕獲與異常的拋出,基礎方法處於調用鏈底端主要在dao、util、daomian、service層中,基礎方法基本不與業務或者流程相關,職責相對單一並存在異常風險。

數據庫操作:例子:1、查詢一個車商;2、保存一個退款...。可能存在的異常或者錯誤:數據庫連接超時;連接數過多,索引主鍵沖突...;

rpc遠程調用第三方接口:例子:1、獲取財務保證金接口、2、根據carId獲取車源信息。可能存在的異常或者錯誤:連接超時,返回結果參數不匹配...。

文件IO操作:

對基礎方法進行異常捕獲,打印錯誤堆棧信息(log),拋出異常(運行時異常,便於調用方理解,而不是堆棧信息)進行業務結束。即log輸出異常堆棧信息,給rd使用,拋出異常進行業務結束與輸出前端或者調用方可理解信息。

基礎方法相當於地基,地基必須是穩固的,完備的。

2、在controller、service與domain層進行業務邏輯開發,業務邏輯開發只專註正常流程,調用基礎方法,不捕獲異常。出現業務異常進行異常拋出,拋出可理解的異常信息與log輸出詳細堆棧錯誤信息。

1、不進行異常捕獲,調用的是基礎方法或者其他業務,在基礎方法已經進行了捕獲與轉換。所以不需要進行異常捕獲。除非某些異常不影響業務或者需要做異常捕獲和拋出(比如,新建退款單的時候會給bd發送一條通知告訴bd,然後bd去登陸處理。發送通知失敗並不會影響業務流程,可以吃掉異常或者捕獲處理)。

2、在業務方法中出現異常的原因主要有兩種:1、傳參與調用其他方法返回值異常。2、業務異常(退保的時候,用戶保證金不足1000)。出現這些情況,直接進行異常拋出,終止業務繼續執行,而不是通過返回值進行返回。至於調用的方法出現異常則不捕獲,因為調用的方法主要是業務方法與基礎方法(基礎方法已經經過處理),直接由高層捕獲。

業務方法相當於房子的內部空間,默認地基是牢固的,外墻是隔絕的,怎麽構造是自己的事,外部無權關系,所以放開專註於業務邏輯。

3、全局異常捕獲,捕獲業務層拋出或者未捕獲的異常進行統一處理,顯示給前端,而不是讓前端看到一個500的內部錯誤。

外部全局捕獲相當於房子的外墻,給外部看到的都是正確與經過處理的,看不到內部的瑕疵。

4、自定義異常可以考慮分為DaoException、DomainException、ServiceException,便於區分與定位異常。

5、在spring boot中使用ControllerAdvice處理全局異。對於Filter中的異常,在aop之前執行,使其跳轉到ExceptionController中處理

需要註意的地方:

1、業務方法進行參數校驗與調用其他方法的參數校驗,校驗失敗拋出異常。

2、業務拋出的異常說明必須通俗易懂,log則要具體詳細。

3、方法必須完備,除拋出異常或者調用其他方法出現異常外,不應該存在自己未知的異常(即自己不能在該方法體中構造而非拋出異常),簡而言之,就是不能寫出可能存在異常的代碼(比如空指針異常)。

4、異常不能吃掉而不做處理,catch方法體內容為空。

5、不要在循環中拋出異常。

6、盡量在高層次捕獲異常。

異常拋出與捕獲的思考