SpringBoot?實現(xiàn)全局異常處理的示例代碼
為什么要使用全局異常處理?
- 減少冗余代碼: 在不使用全局異常處理器的情況下,項目中各層可能會出現(xiàn)大量的try {…} catch {…} finally {…}代碼塊,這些代碼塊不僅冗余,還影響代碼的可讀性。全局異常處理器允許我們在一個獨立的類中定義對所有控制器異常的處理機制,從而消除大部分try-catch塊。
- 統(tǒng)一異常處理: 全局異常處理器可以將異常按階段(如進入Controller前的異常和Service層異常)進行分類處理,提供統(tǒng)一的異常處理策略。
- 自定義異常處理: 對于自定義的異常,使用全局異常處理器可以更容易地進行捕獲和處理,而不是在每個可能拋出異常的地方都寫一遍處理邏輯。
- 客戶端友好性: 直接拋出異常給客戶端可能會導(dǎo)致客戶端無法理解異常信息。全局異常處理器允許我們將異常轉(zhuǎn)換為客戶端可理解的格式(如JSON響應(yīng)),提高用戶體驗。
總結(jié):全局異常處理器的使用可以顯著提高Spring Boot項目的代碼質(zhì)量和可維護性,減少冗余代碼,提高代碼的可讀性和可維護性,統(tǒng)一異常處理策略,并提高客戶端體驗。因此,在Spring Boot項目中,使用全局異常處理器是一個值得推薦的做法。
代碼實戰(zhàn)
首先我們需要在項目中創(chuàng)建一個全局異常處理器類,比如叫 GlobalExceptionHandler,在該類上面需要加上 @RestControllerAdvice 注解表示這是一個全局異常處理器,代碼示例:
import lombok.SneakyThrows; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.servlet.http.HttpServletRequest; import java.util.Optional; /** * 全局異常處理器 */ @RestControllerAdvice public class GlobalExceptionHandler { public final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 攔截參數(shù)驗證異常 */ @SneakyThrows @ExceptionHandler(value = MethodArgumentNotValidException.class) public AjaxResult validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors()); String exceptionStr = Optional.ofNullable(firstFieldError) .map(FieldError::getDefaultMessage) .orElse(StrUtil.EMPTY); logger.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr); return AjaxResult.error(exceptionStr); } /** * 攔截業(yè)務(wù)異常 * BusinessServiceException 是一個自定義異常,以你們項目中的自定義異常為準(zhǔn),如果沒有可以不寫 */ @ExceptionHandler(value = {BusinessServiceException.class}) public AjaxResult handleBusinessException(HttpServletRequest request, BusinessServiceException ex){ String exceptionStr = ex.getMessage(); logger.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr); return AjaxResult.error(exceptionStr); } /** * 攔截未捕獲異常 */ @ExceptionHandler(Throwable.class) public AjaxResult defaultErrorHandler(HttpServletRequest request, Throwable throwable) { logger.error("[{}] {} ", request.getMethod(), getUrl(request), throwable); return AjaxResult.error(throwable.getMessage()); } private String getUrl(HttpServletRequest request) { if (StringUtils.isEmpty(request.getQueryString())) { return request.getRequestURL().toString(); } return request.getRequestURL().toString() + "?" + request.getQueryString(); } }
到這里,全局異常處理器就已經(jīng)寫完了,接下來就是驗證環(huán)節(jié)。我會依次驗證每一個異常場景是否可以成功攔截。
在驗證之前,我們先來看一張圖:
上面這張圖片大家應(yīng)該都很熟悉,這就是在不對接口中的異常進行處理時 SpringBoot 默認返回的頁面,如果我們的異常處理器生效了,那么返回的就應(yīng)該是我們封裝好的數(shù)據(jù)。
驗證 MethodArgumentNotValidException 異常
在驗證 MethodArgumentNotValidException 異常前,需要先說明一下該異常常見的觸發(fā)場景:
- 數(shù)據(jù)轉(zhuǎn)換問題:當(dāng)客戶端傳遞的數(shù)據(jù)類型無法正確轉(zhuǎn)換為方法參數(shù)的類型時,會發(fā)生數(shù)據(jù)轉(zhuǎn)換問題,導(dǎo)致MethodArgumentNotValidException異常。這通常發(fā)生在使用@RequestBody注解處理JSON請求體時,如果JSON格式不正確或無法映射到目標(biāo)對象,就會拋出此異常。
- 錯誤的驗證注解: 如果在實體類中對屬性使用了不符合驗證需求的注解,如@NotNull、@Size等,并且請求中的數(shù)據(jù)不符合這些注解指定的規(guī)則,那么在Spring MVC將請求參數(shù)解析為控制器方法參數(shù)時會觸發(fā)校驗,并拋出MethodArgumentNotValidException異常。
- 未通過校驗的參數(shù): 當(dāng)使用@Valid或@Validated注解對方法參數(shù)進行校驗時,如果參數(shù)不符合校驗規(guī)則(如非空、長度、格式等),會拋出MethodArgumentNotValidException異常。例如,一個表單提交到Controller時,如果表單中的某個字段不符合校驗規(guī)則,則會拋出此異常。
接下來我們創(chuàng)建一個 UserEntity 類做為方法參數(shù)用來測試:
import lombok.Data; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @Data public class UserEntity { @NotNull(message = "用戶ID不能為空") private Long userId; @NotNull(message = "用戶名不能為空") @Size(min = 2, max = 30, message = "用戶名長度必須在2到30個字符之間") private String username; }
// 注意,這個方法我們并沒有采取任何 try catch 操作 @PostMapping("/test") public AjaxResult Test(HttpServletRequest request, @RequestBody @Valid UserEntity userEntity) { Map<String,Object> model = new HashMap<String,Object>(); String planCode = request.getParameter("planCode"); String planDefineId = request.getParameter("planDefineId"); if (StringUtils.isBlank(planDefineId) || StringUtils.isBlank(planCode)) { throw new BusinessServiceException("參數(shù)不可以為空!"); } // 獲取信息 String planName = retailProductCommon.getproductName(planCode, planDefineId); model.put("planName", planName); return AjaxResult.success(model); }
這里我們使用 PostMan 工具去請求這個接口,并設(shè)置一個不滿足要求的參數(shù),看下是否會以為拋錯導(dǎo)致程序中斷。請求結(jié)果:
可以發(fā)現(xiàn),正常返回錯誤信息給客戶端了,并沒有直接拋500。
驗證自定義異常
驗證 BusinessServiceException 異常也很簡單,我們將上面 UserEntity 的參數(shù)補全,但是代碼中為空會拋錯的那個兩個參數(shù)我們不傳,看下會有什么結(jié)果。請求結(jié)果:
這里同樣是我們自定義的返回對象,并不是SpringBoot默認的500頁面,所以驗證成功。
驗證其他異常
接下來我們在 test 接口中手動寫一個異常代碼出來,比如int num = 1/0
當(dāng)代碼執(zhí)行到這里的時候會拋出 / by zero,我們看下結(jié)果如何,會不會被 defaultErrorHandler 方法攔截:
這里同樣是我們自定義的返回對象,并不是SpringBoot默認的500頁面,所以驗證成功。
到此這篇關(guān)于SpringBoot 實現(xiàn)全局異常處理的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot 全局異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java按照List內(nèi)存儲的對象的某個字段進行排序的實例
下面小編就為大家?guī)硪黄狫ava按照List內(nèi)存儲的對象的某個字段進行排序的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12spring boot整合mybatis利用Mysql實現(xiàn)主鍵UUID的方法
這篇文章主要給大家介紹了關(guān)于spring boot整合mybatis利用Mysql實現(xiàn)主鍵UUID的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03maven項目后出現(xiàn)‘parent.relativePath’ of POM錯誤時的解決方法
在Springboot項目啟動時,項目報錯‘parent.relativePath’ of POM問題,項目無法正常啟動,本文就來介紹一下解決方法,感興趣的可以了解一下2023-10-10Mybatis中自定義實例化SqlSessionFactoryBean問題
這篇文章主要介紹了Mybatis中自定義實例化SqlSessionFactoryBean問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02Java使用jacob將微軟office中word、excel、ppt轉(zhuǎn)成pdf
這篇文章主要為大家詳細介紹了Java使用jacob將微軟office中word、excel、ppt轉(zhuǎn)成pdf,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12