SpringBoot中的統(tǒng)一異常處理示例代碼
統(tǒng)一異常處理
統(tǒng)?異常處理使?的是 @ControllerAdvice + @ExceptionHandler 來(lái)實(shí)現(xiàn)的,
@ControllerAdvice 表?控制器通知類(lèi), @ExceptionHandler 是異常處理器,兩個(gè)結(jié)合表
?當(dāng)出現(xiàn)異常的時(shí)候執(zhí)?某個(gè)通知,也就是執(zhí)?某個(gè)方法事件。
代碼示例
ExceptionAdvice
接?返回為數(shù)據(jù)時(shí), 需要加 @ResponseBody 注解?。?!
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import springbook.model.Result;
@Slf4j
@ResponseBody
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler
public Result handlerException(Exception e){
log.error("發(fā)生異常, e: {}", e);
return Result.fail("內(nèi)部錯(cuò)誤");
}
@ExceptionHandler
public Result handlerException(NullPointerException e){
log.error("發(fā)生異常, e:", e);
return Result.fail("發(fā)生空指針異常");
}
@ExceptionHandler
public Result handlerException(ArithmeticException e){
log.error("發(fā)生異常, e:", e);
return Result.fail("發(fā)生算術(shù)異常");
}
}Result
import lombok.Data;
import springbook.enums.ResultStatus;
@Data
public class Result<T> {
private ResultStatus code;
private String errMsg; //錯(cuò)誤信息, 如果業(yè)務(wù)成功, errMsg為空
private T data;
public static <T> Result success(T data){
Result result = new Result<>();
result.setCode(ResultStatus.SUCCESS);
result.setData(data);
return result;
}
public static Result fail(String msg){
Result result = new Result<>();
result.setCode(ResultStatus.FAIl);
result.setErrMsg(msg);
return result;
}
public static Result fail(ResultStatus resultStatus, String msg){
Result result = new Result<>();
result.setCode(resultStatus);
result.setErrMsg(msg);
return result;
}
}TestController
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/test")
@RestController
public class TestController {
@RequestMapping("/t1")
public String t1(){
int result = 10/0;
return "string";
}
@RequestMapping("/t2")
public Integer t2(){
String str = null;
System.out.println(str.length());
return 1;
}
@RequestMapping("/t3")
public Boolean t3(){
Integer[] integers = new Integer[]{1,2,3,4};
System.out.println(integers[5]);
return true;
}
}運(yùn)行結(jié)果
t1的運(yùn)行結(jié)果:

t2的運(yùn)行結(jié)果:

t3的運(yùn)行結(jié)果:

問(wèn)題

我們知道,ArithmeticException和NullPointerException都屬于RuntimeException,同時(shí)RuntimeException又屬于Exception。
那么為什么同時(shí)有處理ArithmeticException和Exception的方法時(shí),針對(duì)ArithmeticException異常,會(huì)調(diào)用處理ArithmeticException異常的方法而不是調(diào)用處理Exception異常的方法?
那么為什么同時(shí)有處理NullPointerException和Exception的方法時(shí),針對(duì)NullPointerException異常,會(huì)調(diào)用處理NullPointerException異常的方法而不是調(diào)用處理Exception異常的方法?
結(jié)合源碼了解問(wèn)題源頭
下面通過(guò)Debug的方式,結(jié)合源碼來(lái)剖析為什么會(huì)出現(xiàn)上面的問(wèn)題。
以上面代碼的訪問(wèn)test/t1路徑下方法導(dǎo)致ArithmeticException異常為例:
經(jīng)過(guò)Debug,我了解到,處理異常會(huì)走到下圖所示的ExceptionHandlerMethodResolver類(lèi)里面的getMapperMethod()方法:

在剛進(jìn)入該方法的時(shí)候,經(jīng)過(guò)第一個(gè)黃色框內(nèi)代碼的處理后,會(huì)把當(dāng)前具有處理異常的方法結(jié)合當(dāng)前發(fā)生的異常進(jìn)行匹配,最終匹配到兩個(gè)合適類(lèi)型的異常,分別是Exception異常和ArithmeticException異常:

但是在經(jīng)過(guò)第二個(gè)黃色框內(nèi)的代碼處理后,會(huì)對(duì)剛剛匹配到的異常進(jìn)行排序,根據(jù)異常的深度進(jìn)行排序,并返回深度最淺的異常(即返回最合適的異常)。
經(jīng)過(guò)第二個(gè)黃色框內(nèi)的代碼處理后,matches里面的內(nèi)容順序發(fā)生變動(dòng):

此時(shí),我們就知道了為什么同時(shí)有處理ArithmeticException和Exception的方法時(shí),針對(duì)ArithmeticException異常,會(huì)調(diào)用處理ArithmeticException異常的方法而不是調(diào)用處理Exception異常的方法;以及為什么同時(shí)有處理NullPointerException和Exception的方法時(shí),針對(duì)NullPointerException異常,會(huì)調(diào)用處理NullPointerException異常的方法而不是調(diào)用處理Exception異常的方法。
優(yōu)點(diǎn)
1. 提高用戶(hù)體驗(yàn)
友好的錯(cuò)誤信息:通過(guò)統(tǒng)一異常處理,可以為前端或其他服務(wù)提供一致的、易于理解的錯(cuò)誤信息,而不是直接暴露后端的堆棧跟蹤或技術(shù)細(xì)節(jié),從而提升用戶(hù)體驗(yàn)。
避免敏感信息泄露:統(tǒng)一處理可以避免在異常響應(yīng)中不小心泄露敏感信息,如數(shù)據(jù)庫(kù)表結(jié)構(gòu)、內(nèi)部實(shí)現(xiàn)細(xì)節(jié)等。
2. 業(yè)務(wù)邏輯和異常處理邏輯解耦
代碼結(jié)構(gòu)清晰:將異常處理邏輯從業(yè)務(wù)邏輯中分離出來(lái),使得業(yè)務(wù)代碼更加專(zhuān)注于業(yè)務(wù)邏輯的實(shí)現(xiàn),而異常處理代碼則專(zhuān)注于異常的捕獲、處理和響應(yīng)。
易于維護(hù):當(dāng)需要修改異常處理邏輯時(shí),只需在統(tǒng)一異常處理類(lèi)中修改,而無(wú)需在多個(gè)業(yè)務(wù)代碼中逐一修改,降低了維護(hù)成本。
3. 減少冗余代碼
避免重復(fù)編寫(xiě):在傳統(tǒng)的Java開(kāi)發(fā)中,異常處理通常是分散在代碼的各個(gè)部分中的。而使用統(tǒng)一異常處理可以避免在每個(gè)可能拋出異常的地方編寫(xiě)重復(fù)的異常處理代碼。
提高代碼復(fù)用性:通過(guò)定義全局的異常處理類(lèi),可以實(shí)現(xiàn)對(duì)多種異常的統(tǒng)一處理,提高代碼的復(fù)用性。
4. 便于代碼風(fēng)格統(tǒng)一
風(fēng)格一致:統(tǒng)一異常處理有助于保持代碼風(fēng)格的一致性,使得整個(gè)項(xiàng)目的代碼更加規(guī)范、整潔。
提升可讀性:對(duì)于其他開(kāi)發(fā)者來(lái)說(shuō),閱讀和理解統(tǒng)一異常處理的代碼也更為容易。
5. 便于異常監(jiān)控和日志記錄
集中監(jiān)控:通過(guò)統(tǒng)一異常處理,可以更容易地實(shí)現(xiàn)對(duì)異常情況的集中監(jiān)控和統(tǒng)計(jì),為后續(xù)的故障排查和性能優(yōu)化提供依據(jù)。
詳細(xì)日志:在異常處理過(guò)程中,可以記錄詳細(xì)的異常信息和上下文信息,便于后續(xù)的日志分析和問(wèn)題定位。
到此這篇關(guān)于SpringBoot中的統(tǒng)一異常處理示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot統(tǒng)一異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java基于swing實(shí)現(xiàn)的連連看代碼
這篇文章主要介紹了java基于swing實(shí)現(xiàn)的連連看代碼,包含了游戲中涉及的事件處理與邏輯功能,需要的朋友可以參考下2014-11-11
SchedulingConfigurer實(shí)現(xiàn)動(dòng)態(tài)定時(shí),導(dǎo)致ApplicationRunner無(wú)效解決
這篇文章主要介紹了SchedulingConfigurer實(shí)現(xiàn)動(dòng)態(tài)定時(shí),導(dǎo)致ApplicationRunner無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05
ireport數(shù)據(jù)表格報(bào)表的簡(jiǎn)單使用
這篇文章給大家介紹了如何畫(huà)一個(gè)報(bào)表模板,這里介紹下畫(huà)表格需要用到的組件,文中通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-10-10
Spring實(shí)現(xiàn)聲明式事務(wù)的方法詳解
這篇文章主要介紹了Spring實(shí)現(xiàn)聲明式事務(wù)的方法詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Java泛型枚舉Annotation接口詳細(xì)解讀與Eclipse發(fā)展
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08
SpringBoot集成Nacos的實(shí)戰(zhàn)完全指南
Nacos?作為阿里巴巴開(kāi)源的一站式微服務(wù)管理平臺(tái),已成為?Spring?Cloud?Alibaba?生態(tài)的核心組件,本文基于?Spring?Boot?3.x?+?Nacos?2.x?最新版本,系統(tǒng)講解?Nacos?的安裝部署、配置中心、服務(wù)發(fā)現(xiàn)等核心功能,并提供生產(chǎn)級(jí)最佳實(shí)踐方案,有需要的小伙伴可以了解下2026-02-02
Docker環(huán)境下Spring Boot應(yīng)用內(nèi)存飆升分析與解決場(chǎng)景分析
當(dāng)運(yùn)行一個(gè)Spring Boot項(xiàng)目時(shí),如果未設(shè)置JVM內(nèi)存參數(shù),Spring Boot默認(rèn)會(huì)采用JVM自身默認(rèn)的配置策略,接下來(lái)通過(guò)本文給大家介紹Docker環(huán)境下Spring Boot應(yīng)用內(nèi)存飆升分析與解決方法,需要的朋友參考下吧2021-08-08

