SpringBoot?實(shí)現(xiàn)全局異常處理的示例代碼
為什么要使用全局異常處理?
- 減少冗余代碼: 在不使用全局異常處理器的情況下,項(xiàng)目中各層可能會(huì)出現(xiàn)大量的try {…} catch {…} finally {…}代碼塊,這些代碼塊不僅冗余,還影響代碼的可讀性。全局異常處理器允許我們?cè)谝粋€(gè)獨(dú)立的類中定義對(duì)所有控制器異常的處理機(jī)制,從而消除大部分try-catch塊。
- 統(tǒng)一異常處理: 全局異常處理器可以將異常按階段(如進(jìn)入Controller前的異常和Service層異常)進(jìn)行分類處理,提供統(tǒng)一的異常處理策略。
- 自定義異常處理: 對(duì)于自定義的異常,使用全局異常處理器可以更容易地進(jìn)行捕獲和處理,而不是在每個(gè)可能拋出異常的地方都寫一遍處理邏輯。
- 客戶端友好性: 直接拋出異常給客戶端可能會(huì)導(dǎo)致客戶端無(wú)法理解異常信息。全局異常處理器允許我們將異常轉(zhuǎn)換為客戶端可理解的格式(如JSON響應(yīng)),提高用戶體驗(yàn)。
總結(jié):全局異常處理器的使用可以顯著提高Spring Boot項(xiàng)目的代碼質(zhì)量和可維護(hù)性,減少冗余代碼,提高代碼的可讀性和可維護(hù)性,統(tǒng)一異常處理策略,并提高客戶端體驗(yàn)。因此,在Spring Boot項(xiàng)目中,使用全局異常處理器是一個(gè)值得推薦的做法。
代碼實(shí)戰(zhàn)
首先我們需要在項(xiàng)目中創(chuàng)建一個(gè)全局異常處理器類,比如叫 GlobalExceptionHandler,在該類上面需要加上 @RestControllerAdvice 注解表示這是一個(gè)全局異常處理器,代碼示例:
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ù)驗(yàn)證異常 */ @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 是一個(gè)自定義異常,以你們項(xiàng)目中的自定義異常為準(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)寫完了,接下來(lái)就是驗(yàn)證環(huán)節(jié)。我會(huì)依次驗(yàn)證每一個(gè)異常場(chǎng)景是否可以成功攔截。
在驗(yàn)證之前,我們先來(lái)看一張圖:
上面這張圖片大家應(yīng)該都很熟悉,這就是在不對(duì)接口中的異常進(jìn)行處理時(shí) SpringBoot 默認(rèn)返回的頁(yè)面,如果我們的異常處理器生效了,那么返回的就應(yīng)該是我們封裝好的數(shù)據(jù)。
驗(yàn)證 MethodArgumentNotValidException 異常
在驗(yàn)證 MethodArgumentNotValidException 異常前,需要先說(shuō)明一下該異常常見的觸發(fā)場(chǎng)景:
- 數(shù)據(jù)轉(zhuǎn)換問(wèn)題:當(dāng)客戶端傳遞的數(shù)據(jù)類型無(wú)法正確轉(zhuǎn)換為方法參數(shù)的類型時(shí),會(huì)發(fā)生數(shù)據(jù)轉(zhuǎn)換問(wèn)題,導(dǎo)致MethodArgumentNotValidException異常。這通常發(fā)生在使用@RequestBody注解處理JSON請(qǐng)求體時(shí),如果JSON格式不正確或無(wú)法映射到目標(biāo)對(duì)象,就會(huì)拋出此異常。
- 錯(cuò)誤的驗(yàn)證注解: 如果在實(shí)體類中對(duì)屬性使用了不符合驗(yàn)證需求的注解,如@NotNull、@Size等,并且請(qǐng)求中的數(shù)據(jù)不符合這些注解指定的規(guī)則,那么在Spring MVC將請(qǐng)求參數(shù)解析為控制器方法參數(shù)時(shí)會(huì)觸發(fā)校驗(yàn),并拋出MethodArgumentNotValidException異常。
- 未通過(guò)校驗(yàn)的參數(shù): 當(dāng)使用@Valid或@Validated注解對(duì)方法參數(shù)進(jìn)行校驗(yàn)時(shí),如果參數(shù)不符合校驗(yàn)規(guī)則(如非空、長(zhǎng)度、格式等),會(huì)拋出MethodArgumentNotValidException異常。例如,一個(gè)表單提交到Controller時(shí),如果表單中的某個(gè)字段不符合校驗(yàn)規(guī)則,則會(huì)拋出此異常。
接下來(lái)我們創(chuàng)建一個(gè) UserEntity 類做為方法參數(shù)用來(lái)測(cè)試:
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 = "用戶名長(zhǎng)度必須在2到30個(gè)字符之間") private String username; }
// 注意,這個(gè)方法我們并沒有采取任何 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 工具去請(qǐng)求這個(gè)接口,并設(shè)置一個(gè)不滿足要求的參數(shù),看下是否會(huì)以為拋錯(cuò)導(dǎo)致程序中斷。請(qǐng)求結(jié)果:
可以發(fā)現(xiàn),正常返回錯(cuò)誤信息給客戶端了,并沒有直接拋500。
驗(yàn)證自定義異常
驗(yàn)證 BusinessServiceException 異常也很簡(jiǎn)單,我們將上面 UserEntity 的參數(shù)補(bǔ)全,但是代碼中為空會(huì)拋錯(cuò)的那個(gè)兩個(gè)參數(shù)我們不傳,看下會(huì)有什么結(jié)果。請(qǐng)求結(jié)果:
這里同樣是我們自定義的返回對(duì)象,并不是SpringBoot默認(rèn)的500頁(yè)面,所以驗(yàn)證成功。
驗(yàn)證其他異常
接下來(lái)我們?cè)?test 接口中手動(dòng)寫一個(gè)異常代碼出來(lái),比如int num = 1/0
當(dāng)代碼執(zhí)行到這里的時(shí)候會(huì)拋出 / by zero,我們看下結(jié)果如何,會(huì)不會(huì)被 defaultErrorHandler 方法攔截:
這里同樣是我們自定義的返回對(duì)象,并不是SpringBoot默認(rèn)的500頁(yè)面,所以驗(yàn)證成功。
到此這篇關(guān)于SpringBoot 實(shí)現(xiàn)全局異常處理的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot 全局異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java按照List內(nèi)存儲(chǔ)的對(duì)象的某個(gè)字段進(jìn)行排序的實(shí)例
下面小編就為大家?guī)?lái)一篇Java按照List內(nèi)存儲(chǔ)的對(duì)象的某個(gè)字段進(jìn)行排序的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12spring boot整合mybatis利用Mysql實(shí)現(xiàn)主鍵UUID的方法
這篇文章主要給大家介紹了關(guān)于spring boot整合mybatis利用Mysql實(shí)現(xiàn)主鍵UUID的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-03-03maven項(xiàng)目后出現(xiàn)‘parent.relativePath’ of POM錯(cuò)誤時(shí)的解決方法
在Springboot項(xiàng)目啟動(dòng)時(shí),項(xiàng)目報(bào)錯(cuò)‘parent.relativePath’ of POM問(wèn)題,項(xiàng)目無(wú)法正常啟動(dòng),本文就來(lái)介紹一下解決方法,感興趣的可以了解一下2023-10-10Mybatis中自定義實(shí)例化SqlSessionFactoryBean問(wèn)題
這篇文章主要介紹了Mybatis中自定義實(shí)例化SqlSessionFactoryBean問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02Java中FileWriter類的簡(jiǎn)介說(shuō)明
這篇文章主要介紹了Java中FileWriter類的簡(jiǎn)介說(shuō)明,FileWriter類提供了多種寫入字符的方法,包括寫入單個(gè)字符、寫入字符數(shù)組和寫入字符串等,它還提供了一些其他的方法,如刷新緩沖區(qū)、關(guān)閉文件等,需要的朋友可以參考下2023-10-10java實(shí)現(xiàn)稀疏矩陣的壓縮與解壓的方法
這篇文章主要介紹了java實(shí)現(xiàn)稀疏矩陣的壓縮與解壓 ,把該稀疏矩陣壓縮以三元組形式表示并以文件形式保存,再寫另一個(gè)程序讀取文件中的信息把壓縮后的三元組還原成原來(lái)的稀疏矩陣,需要的朋友可以參考下2022-03-03Java使用jacob將微軟office中word、excel、ppt轉(zhuǎn)成pdf
這篇文章主要為大家詳細(xì)介紹了Java使用jacob將微軟office中word、excel、ppt轉(zhuǎn)成pdf,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12