Spring Boot異常處理try-catch應(yīng)該怎么使用?
1. 為什么異常值得單獨(dú)寫(xiě)一整個(gè)系列
在多數(shù)業(yè)務(wù)代碼中,異常往往被當(dāng)作一種“不得不寫(xiě)的語(yǔ)法”:
try {
service.process();
} catch (Exception e) {
log.error("error", e);
}
但在真實(shí)的 Web 系統(tǒng)中,異常從來(lái)不是一個(gè)語(yǔ)法問(wèn)題,而是一個(gè)系統(tǒng)失控時(shí)的兜底機(jī)制。
一次 HTTP 請(qǐng)求,往往會(huì)經(jīng)歷如下鏈路:
- Filter
- Interceptor
- Controller
- 參數(shù)綁定與校驗(yàn)
- Service
- DAO
- 事務(wù)
- AOP 代理
- JSON 序列化
異??梢栽谌魏我粋€(gè)環(huán)節(jié)出現(xiàn),而且一旦出現(xiàn),就會(huì)“逆著調(diào)用棧”向上冒泡,最終由框架決定:
- 是否回滾事務(wù)
- 是否返回 500
- 返回什么格式
- 是否記錄堆棧
- 是否暴露錯(cuò)誤信息給前端
因此,異常不是“邊角料”,而是整個(gè)調(diào)用鏈的終點(diǎn)匯合處。
2. Java 異常模型的關(guān)鍵認(rèn)知(只講對(duì)后面有用的)
2.1 Throwable 體系結(jié)構(gòu)
Throwable
├── Error
└── Exception
├── RuntimeException
└── Checked Exception
- Error:JVM 級(jí)錯(cuò)誤,應(yīng)用通常無(wú)能為力
- Exception:應(yīng)用級(jí)異常
- RuntimeException:Spring 默認(rèn)認(rèn)為這是“不可恢復(fù)異常”
- Checked Exception:需要顯式聲明和處理
Spring 事務(wù)為什么默認(rèn)只對(duì) RuntimeException 回滾?
這個(gè)問(wèn)題在后面的事務(wù)異常章節(jié)會(huì)專門(mén)展開(kāi)。
2.2 異常傳播是“反向調(diào)用鏈”
正常調(diào)用是:
Controller → Service → DAO
異常傳播是:
DAO 拋異常 → Service → Controller → 框架
誰(shuí)最后接住異常,誰(shuí)就擁有最終解釋權(quán)。
3. 為什么 Web 系統(tǒng)不能到處 try-catch
3.1 try-catch 的三個(gè)常見(jiàn)問(wèn)題
- 吞異常,導(dǎo)致問(wèn)題被掩蓋
- 重復(fù)代碼,Controller 層異常處理泛濫
- 破壞事務(wù)回滾邏輯
例如:
@Transactional
public void createOrder() {
try {
saveOrder();
} catch (Exception e) {
log.error("error", e);
}
}
這個(gè)代碼看起來(lái)穩(wěn)健,實(shí)際上事務(wù)已經(jīng)無(wú)法回滾。
3.2 異常必須“集中處理”
在 Web 架構(gòu)中,有一個(gè)非常重要的設(shè)計(jì)原則:
異常應(yīng)該在“邊界層”統(tǒng)一處理,而不是在業(yè)務(wù)層消化。
Spring MVC 正是基于這個(gè)原則,設(shè)計(jì)了一整套異常處理機(jī)制。
4. Spring MVC 的異常處理總體設(shè)計(jì)思想
4.1 正常流程 vs 異常流程
正常流程:
請(qǐng)求 → Handler → 返回值 → 響應(yīng)
異常流程:
請(qǐng)求 → Handler → 拋異常 → 異常解析 → 響應(yīng)
Spring MVC 的核心設(shè)計(jì)點(diǎn)在于:
異常不是 if-else 分支,而是一條獨(dú)立的處理鏈路。
4.2 異常處理在 DispatcherServlet 中的位置
DispatcherServlet 是整個(gè) MVC 的“總控中樞”。
在其核心方法 doDispatch 中,異常被統(tǒng)一捕獲:
try {
// 查找 Handler 并執(zhí)行
} catch (Exception ex) {
dispatchException = ex;
}
這意味著:
- Controller 不需要感知異常如何返回
- 框架會(huì)統(tǒng)一接管異常
5. Spring MVC 的三種基礎(chǔ)異常處理方式
5.1 直接拋出異常(推薦)
@GetMapping("/order")
public Order getOrder() {
throw new IllegalArgumentException("參數(shù)錯(cuò)誤");
}
異常會(huì)交給框架處理,而不是在 Controller 內(nèi)部解決。
5.2 @ExceptionHandler:局部異常處理
@RestController
public class OrderController {
@ExceptionHandler(IllegalArgumentException.class)
public String handleIllegalArg(Exception e) {
return e.getMessage();
}
}
特點(diǎn):
- 只對(duì)當(dāng)前 Controller 生效
- 適合非常局部的異常場(chǎng)景
5.3 @ControllerAdvice:全局異常處理(重點(diǎn))
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ErrorResponse handle(Exception e) {
return new ErrorResponse("500", e.getMessage());
}
}
這是 Spring Boot 項(xiàng)目中最常見(jiàn)的異常處理入口。
6. @ControllerAdvice 的設(shè)計(jì)價(jià)值
6.1 為什么它是“全局異常處理”的核心
@ControllerAdvice 本質(zhì)上解決了三個(gè)問(wèn)題:
- 異常集中管理
- 返回格式統(tǒng)一
- 與業(yè)務(wù)邏輯解耦
6.2 多個(gè) ControllerAdvice 的順序問(wèn)題
Spring 支持定義多個(gè)全局異常處理器:
@Order(1)
@RestControllerAdvice
class BizExceptionHandler {}
@Order(2)
@RestControllerAdvice
class SystemExceptionHandler {}
優(yōu)先級(jí)越小,越先執(zhí)行。
7. 異常處理的第一版架構(gòu)形態(tài)
在“入門(mén)階段”,一個(gè)相對(duì)合理的異常架構(gòu)通常是:
Controller ↓ 拋異常 ↓ @ControllerAdvice ↓ 統(tǒng)一錯(cuò)誤響應(yīng)
對(duì)應(yīng)的返回結(jié)構(gòu)示例:
{
"code": "SYSTEM_ERROR",
"message": "系統(tǒng)異常,請(qǐng)稍后再試"
}
8. 異常處理流程圖(概覽)
圖1 Spring MVC 異常處理基本流程圖
9. 本篇小結(jié)(從入門(mén)視角看異常)
到這里,我們只做了三件事:
- 糾正“異常只是 try-catch”的認(rèn)知
- 明確異常是 Web 系統(tǒng)的統(tǒng)一出口
- 理解 Spring MVC 為什么要集中處理異常
但我們還沒(méi)有回答幾個(gè)關(guān)鍵問(wèn)題:
- 異常是如何一步步被解析的?
- 為什么 @ExceptionHandler 能生效?
- Spring Boot 的 /error 是干什么的?
- 為什么有些異常進(jìn)不了 ControllerAdvice?
?? 這些問(wèn)題,都需要進(jìn)入源碼層面才能解釋清楚。
到此這篇關(guān)于Spring Boot異常處理try-catch應(yīng)該怎么使用?的文章就介紹到這了,更多相關(guān)Spring Boot異常處理try-catch內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
參考資料
- Spring Framework Reference – Exception Handling
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-exceptionhandler.html - Spring MVC 源碼(DispatcherServlet)
https://github.com/spring-projects/spring-framework
相關(guān)文章
SpringBoot整合Swagger3生成接口文檔過(guò)程解析
這篇文章主要介紹了SpringBoot整合Swagger3生成接口文檔過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
阿里SpringBoot應(yīng)用自動(dòng)化部署實(shí)現(xiàn)IDEA版Jenkins
這篇文章主要為大家介紹了阿里SpringBoot應(yīng)用自動(dòng)化部署實(shí)現(xiàn)IDEA版Jenkins過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
JAVA中時(shí)間戳與LocalDateTime互相轉(zhuǎn)換代碼例子
最近在編碼過(guò)程中遇到將時(shí)間戳轉(zhuǎn)化為 LocalDateTime,所以這里給總結(jié)下,這篇文章主要給大家介紹了關(guān)于JAVA中時(shí)間戳與LocalDateTime互相轉(zhuǎn)換的相關(guān)資料,需要的朋友可以參考下2023-11-11
Java中sharding-jdbc 綁定表的實(shí)現(xiàn)
綁定表是 ShardingSphere-JDBC 解決跨庫(kù)表關(guān)聯(lián)查詢的核心機(jī)制,通過(guò)強(qiáng)制分片鍵和算法一致的表路由到同一物理庫(kù),實(shí)現(xiàn)高效本地關(guān)聯(lián),適用于大表之間的關(guān)聯(lián)場(chǎng)景,避免了全局表的存儲(chǔ)冗余,下面就來(lái)詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2026-03-03
Day16基礎(chǔ)不牢地動(dòng)山搖-Java基礎(chǔ)
這篇文章主要給大家介紹了關(guān)于Java中方法使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-08-08
Java高級(jí)特性之反射機(jī)制實(shí)例詳解
這篇文章主要介紹了Java高級(jí)特性之反射機(jī)制,結(jié)合實(shí)例形式詳細(xì)分析了Java反射機(jī)制原理、功能、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2018-08-08
SpringBoot多數(shù)據(jù)源配置方式以及報(bào)錯(cuò)問(wèn)題的解決
這篇文章主要介紹了SpringBoot多數(shù)據(jù)源配置方式以及報(bào)錯(cuò)問(wèn)題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

