SpringBoot異常處理的簡單理解

Springboot異常處理:

1、局部異常處理:

直接對Controller類進行操作

 

2、全局統一異常處理

Springboot兩種全局異常統一處理的方式:

A使用繼承BasicErrorController 來實現

B通過@ControllerAdvice 註解來處理統一錯誤(Advice 異常處理)

 

(1)Java 異常的 Root 是 Throwable, 其下有 Error 和 Exception. Error 是 JVM 級的致命錯誤, 應用系統內部一般不用關心這類錯誤. Exception 是異常的父類, 其下分爲兩類, 一類是 Runtime Exception, 一類是 Checked Exception. Checked Exception 是那些編譯器能檢查到的異常, 如果一個函數中拋出了這類異常, 我們要麼 catch 它, 要麼在函數簽名上繼續拋出去, 否則程序將不能編譯通過。

Runtime Exception 有, 包括 RuntimeException 和它的子類. 比如 ArrayIndexOutOfBoundsException/ClassCastException/被 0 除等. 
Checked Exception 有: Exception 類和所有非 RuntimeException 類的異常都屬於 checked exception, 比如 IoException 等.

(2)dao、service、controller出現的未知異常都通過throws Exception向上拋出,最後由springmvc前端控制器交由異常處理器進行異常處理,springmvc提供全局異常處理器進行統一的異常處理,一個系統只有一個異常處理器。Springboot也有統一的異常處理器ControllerAdvice。

一般思路:系統有自定義異常時,在程序中手動拋出throw MyException,對於未知異常通過在函數上聲明throws Exception向上拋出,dao拋給service,service再拋給Controller,最後Controller拋給前端控制器,前端控制器調用全局異常處理器進行處理。

(3)異常處理的一般實踐步驟。

===================================
自定義類的最佳實踐:
===================================
1. 先定義一個基類 BusinessException, 繼承自 RuntimeException. 
2. 定義一套BusinessErrorCode 枚舉類型, 包含 BusinessErrorCode/HttpStatus/BusinessErrorMessage, 這裏的 BusinessErrorCode 不同於 HttpStatus, 它是業務上的錯誤代碼 (int 型). 
2. 在 BusinessException 基類上, 加上綁定 BusinessErrorCode 枚舉類型的機制. 
3. 基於 BusinessException 定義一組子類, 比如 UserNotLoginException/PermissionForbiddenException/DataNotFoundException 等等, 並將這些子類加入到一個 BusinessExceptionEnum 枚舉中, 方便使用.

 

===================================
Spring 項目數據驗證最佳實踐
===================================
1. 針對 UI 輸入檢查, 如果 js 前端檢查有困難, 可以在 Controller 層使用 Pojo validation 手段做檢查, 然後前端使用 ajax 拿到校驗結果. 檢查過程沒有觸發 UI 完整渲染, 用戶體驗會很好. 
2. Controller 層使用 validation 進行檢查, 可以在視圖函數的形參上檢查, 或者在視圖函數內部檢查. 
3. Service 層, 使用 org.springframework.util.Assert 進行數據驗證, 比如 Assert.notNull(user, "user is not null.");
4. DAO 層, 不做任何數據驗證, 因爲所有數據問題應該在Service層或Controller層做個驗證.


===================================
Spring 各層封裝的手法
===================================
1. DAO 層, 函數的形參最好以 DO 類做參數, 而不是傳入很多個字段參數. 這樣的好處是, 避免Table增刪字段, DAO層函數定義也要跟着修改, 上層的調用代碼也要修改. 
2. DAO 層, insert 和 update 要獨立爲兩個函數. 到底是新增還是更新, 應由 Service 層進行邏輯控制. 
3. Service 層函數的形參, 到底是使用 DO 類, 還是簡單的屬性清單, 看具體情況吧.


===================================
Spring 日誌和異常處理的最佳實踐
===================================
異常處理的基本思路是: 早拋出, 晚捕獲. 日誌輸出的基本思路是, 詳盡但不冗餘. 
落實到具體的項目中, 在不同分層中, 應採用不同的規則, 一般的分層有: DAO -> Service -> Controller -> 統一異常 Controller 層.

1. DAO 層: 
   (1) 儘量不 catch 任何異常, 該向上拋就拋. 
   (2) 不用記錄 log 日誌, 或者僅使用 logger.debug() 記錄

2. Service 層的做法: 
    (1) @Transactional 註解應該加在 Service 層上. 
    (2) 對於一些關鍵問題, 比如 Checked Exception 或者數據的問題, 應該及時 throw new BusinessException 異常, 以確保事務完整. 
    (3) throw new BusinessException 時的日誌, 爲了避免日誌重複, 不需要 log 日誌輸出. 
    (4) Service 層一般的日誌級別, 應該用 logger.debug() 記日誌. 
    (5) Service 層函數的返回值應該是 Optional 類型, 方便 Controller 做 null 判斷.


3. Controller 層: 
   (1) Controller 層負責組裝 Service, 在關鍵步驟上應該加日誌輸出 (info 級別) 
   (2) Controller 層不應再主動 throw 異常. 
4. 統一異常 Controller 層: 
   (1) 通過 json 或 UI 返回詳細的報錯信息, 包括 HttpStatus 和詳盡的 BusinessErrorCode/BusinessErrorMessage 以及 DetailErrorMessage(來源於 exception.getMessage()), 甚至包括 exception 的 stack trace. 
   (2) 對於 Exception 類的異常, 說明這是我們預料之外的報錯, 應該使用 logger.error() 級別記錄; 
   (3) 對於 BusinessException 和子類的異常, 則說明我們的程序已經預料到了, 事物該回滾也已經回滾了, 所以應該以 logger.warn() 或 logger.info() 記錄日誌. 
   (4) 對於 Spring Boot 缺省的 /error 進行定製, 增加一些"系統主頁"和"返回"的鏈接, 改善用戶體驗.

(4)一些重要的注意事項:

Adao層一般不需要拋出異常,有事務的情況下,會將service的異常拋出到控制層做處理的,不然可能會影響事務的回滾,如果不存在事務,則可以直接在service層進行處理。

B針對預期可能發生的異常(檢查類型(checked)),在代碼手動處理異常可以try/catch捕獲,可以向上拋出,可以聲明。

try-catch 是在當前位置處理異常,嘗試能不能正常的走完整個作用域,如果不能則拋出一個異常。
throws是向上拋出異常,用來聲明一個方法可能產生的所有異常,不做任何處理而是將異常往上傳,誰調用我我就拋給誰,throws在方法後邊聲明異常,其實就是自己不想對異常做出任何的處理,告訴別人自己可能出現的異常,交給別人處理

throw 就是拋出一個具體的異常,並獲取這個異常的引用,這個異常會被拋到外部的環境,由外部環境進行處理。

 

C 在service中如果聲明瞭@Transactional,但是又在方法裏面自己捕獲了異常,也就是try catch掉了,那就不會回滾了,因爲切入點根本沒捕獲到,也談不上調用增強處理中的方法了。解決:1.拋出RuntimeException 2.拋出Exception,同時在事務聲明中加上@Transactional(rollbackFor = Exception.class)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章