Spring Boot全局異常處理保姆級(jí)教程從入門到實(shí)戰(zhàn)(看完秒懂)
在Spring Boot開發(fā)中,你是否遇到過這樣的困擾?
每個(gè)Controller方法都要寫一堆try-catch塊,代碼冗余到懷疑人生;前端抱怨接口返回格式不統(tǒng)一,有的返回JSON,有的返回HTML;系統(tǒng)報(bào)錯(cuò)時(shí)直接把數(shù)據(jù)庫異常堆棧暴露給用戶,安全隱患拉滿……別慌!今天這篇文章帶你徹底搞定全局異常處理,用一行代碼替代所有重復(fù)的
try-catch,讓異常處理變得優(yōu)雅又高效!
一、為什么要學(xué)全局異常處理?看完你就懂了!
1.1 痛點(diǎn)驅(qū)動(dòng)
想象一下:你有10個(gè)Controller,每個(gè)都要處理NullPointerException、IllegalArgumentException,甚至還要處理業(yè)務(wù)自定義的“用戶不存在”異常……代碼重復(fù)率高達(dá)80%,改個(gè)返回格式就得改10個(gè)地方,這不是在寫代碼,是在“復(fù)制粘貼”!
1.2 核心價(jià)值
- 代碼簡(jiǎn)潔:只需一個(gè)類集中處理所有異常,告別重復(fù)
try-catch。 - 響應(yīng)統(tǒng)一:前端收到的永遠(yuǎn)是
{code: 200, msg: "成功", data: ...}格式,解析無壓力。 - 安全可控:系統(tǒng)異常(如數(shù)據(jù)庫崩潰)不暴露堆棧,業(yè)務(wù)異常(如“余額不足”)明確提示用戶。
- 日志友好:異常信息集中記錄,排查問題不用翻遍各個(gè)Controller。
二、Spring Boot全局異常處理的核心實(shí)現(xiàn)
Spring Boot提供了超好用的@RestControllerAdvice注解(@ControllerAdvice+@ResponseBody的組合),專門用于前后端分離場(chǎng)景的全局異常處理。咱們直接上干貨!
2.1 第一步:定義統(tǒng)一響應(yīng)格式
前端需要“標(biāo)準(zhǔn)化”的錯(cuò)誤提示,所以先定義一個(gè)通用的Result類,所有接口返回值都用它包裝。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code; // 狀態(tài)碼(200=成功,400=參數(shù)錯(cuò)誤,500=系統(tǒng)錯(cuò)誤)
private String msg; // 提示信息
private T data; // 業(yè)務(wù)數(shù)據(jù)(可選)
// 快速構(gòu)建成功響應(yīng)(帶數(shù)據(jù))
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
// 快速構(gòu)建失敗響應(yīng)(自定義code和msg)
public static <T> Result<T> error(Integer code, String msg) {
return new Result<>(code, msg, null);
}
}2.2 第二步:編寫全局異常處理器
用@RestControllerAdvice標(biāo)記一個(gè)類,Spring Boot會(huì)自動(dòng)掃描并攔截所有Controller的異常。重點(diǎn)是用@ExceptionHandler指定要處理的異常類型。
場(chǎng)景1:處理業(yè)務(wù)自定義異常(比如“用戶不存在”)
業(yè)務(wù)中經(jīng)常需要拋“用戶不存在”、“訂單已支付”這類異常,咱們可以自定義異常類,然后在全局處理器里捕獲。
步驟1:定義業(yè)務(wù)異常類
// 自定義業(yè)務(wù)異常(繼承RuntimeException,方便在Service層拋出)
public class BusinessException extends RuntimeException {
private Integer code; // 業(yè)務(wù)錯(cuò)誤碼(如4001=用戶不存在)
public BusinessException(Integer code, String msg) {
super(msg);
this.code = code;
}
public Integer getCode() {
return code;
}
}步驟2:全局捕獲業(yè)務(wù)異常
@RestControllerAdvice
public class GlobalExceptionHandler {
// 處理業(yè)務(wù)異常(比如用戶不存在)
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
}場(chǎng)景2:處理參數(shù)校驗(yàn)異常(@Valid校驗(yàn)失敗)
用@Valid校驗(yàn)請(qǐng)求參數(shù)時(shí),參數(shù)不合法會(huì)拋MethodArgumentNotValidException,咱們可以提取錯(cuò)誤信息,友好提示用戶。
// 全局處理參數(shù)校驗(yàn)異常(@RequestBody參數(shù)校驗(yàn)失?。?
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidationException(MethodArgumentNotValidException e) {
// 提取所有校驗(yàn)失敗的字段信息(比如"用戶名不能為空")
String errorMsg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(";"));
return Result.error(400, "參數(shù)校驗(yàn)失?。? + errorMsg);
}場(chǎng)景3:處理系統(tǒng)未知異常(兜底方案)
數(shù)據(jù)庫崩潰、空指針等系統(tǒng)級(jí)異常,必須捕獲并返回通用提示,同時(shí)記錄完整日志(避免暴露敏感信息)。
// 全局處理其他未捕獲的異常(系統(tǒng)級(jí)錯(cuò)誤)
@ExceptionHandler(Exception.class)
public Result<?> handleSystemException(Exception e) {
// 記錄完整異常堆棧(生產(chǎn)環(huán)境必加?。?
log.error("系統(tǒng)發(fā)生未知異常,請(qǐng)求路徑:{}", getRequestPath(), e);
// 返回友好提示(前端看到的是"服務(wù)器繁忙,請(qǐng)稍后再試")
return Result.error(500, "服務(wù)器繁忙,請(qǐng)稍后再試");
}
// 輔助方法:獲取當(dāng)前請(qǐng)求路徑(需要spring-web依賴)
private String getRequestPath() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
return attributes == null ? "" : attributes.getRequest().getRequestURI();
}2.3 第三步:在業(yè)務(wù)中拋異常,測(cè)試效果!
現(xiàn)在,在Service層拋出自定義異常,看看是否被全局處理器捕獲。
@Service
public class UserService {
public User getUserById(Long id) {
// 模擬數(shù)據(jù)庫查詢(假設(shè)id=100的用戶不存在)
User user = null;
if (user == null) {
throw new BusinessException(4001, "用戶ID=" + id + "不存在"); // 拋業(yè)務(wù)異常
}
return user;
}
}
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return Result.success(user); // 正常返回
}
}測(cè)試結(jié)果:
- 當(dāng)
id=100時(shí),返回{"code":4001,"msg":"用戶ID=100不存在","data":null}(業(yè)務(wù)異常被捕獲)。 - 當(dāng)
id=1(存在用戶)時(shí),返回{"code":200,"msg":"操作成功","data":{...}}(正常響應(yīng))。
三、進(jìn)階玩法:這些場(chǎng)景也能輕松搞定!
3.1 自定義HTTP狀態(tài)碼(比如404)
如果想讓某些異常返回特定的HTTP狀態(tài)碼(如“資源不存在”返回404),可以用@ResponseStatus注解。
// 自定義異常(標(biāo)記為404狀態(tài)碼)
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String msg) {
super(msg);
}
}
// 全局處理(可選,也可以直接用@ResponseStatus)
@ExceptionHandler(ResourceNotFoundException.class)
public Result<?> handleResourceNotFound(ResourceNotFoundException e) {
return Result.error(404, e.getMessage());
}3.2 處理表單參數(shù)校驗(yàn)異常(@ModelAttribute)
如果是表單提交(非@RequestBody),參數(shù)校驗(yàn)失敗會(huì)拋BindException,處理方式和MethodArgumentNotValidException類似:
@ExceptionHandler(BindException.class)
public Result<?> handleBindException(BindException e) {
String errorMsg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(";"));
return Result.error(400, "參數(shù)校驗(yàn)失?。? + errorMsg);
}3.3 記錄異常日志的細(xì)節(jié)
全局處理中記錄日志時(shí),建議帶上請(qǐng)求路徑、用戶ID(如果有登錄)等信息,方便排查問題:
@ExceptionHandler(Exception.class)
public Result<?> handleSystemException(Exception e) {
// 獲取請(qǐng)求路徑
String path = getRequestPath();
// 獲取用戶ID(假設(shè)從Token中解析)
String userId = SecurityUtils.getCurrentUserId();
// 記錄日志(包含關(guān)鍵上下文信息)
log.error("系統(tǒng)異常 | 用戶ID={} | 路徑={} | 異常信息={}",
userId, path, e.getMessage(), e);
return Result.error(500, "服務(wù)器繁忙,請(qǐng)稍后再試");
}四、避坑指南:這些坑別踩!
4.1 全局異常不生效?
- 檢查是否添加了
@RestControllerAdvice注解(別漏了@ResponseBody)。 - 檢查異常類的包路徑是否被Spring掃描到(確保全局處理器和業(yè)務(wù)類在同一個(gè)或子包下)。
- 檢查是否有局部
try-catch覆蓋了全局處理(比如Controller里自己catch了異常,沒拋出去)。
4.2 生產(chǎn)環(huán)境暴露敏感信息?
- 系統(tǒng)異常(如
SQLException)的msg不要直接返回給前端,用“服務(wù)器繁忙”代替。 - 日志中可以記錄完整堆棧,但響應(yīng)體里只保留友好提示。
4.3 自定義異常沒被捕獲?
- 確保自定義異常是
RuntimeException的子類(Spring默認(rèn)只捕獲RuntimeException和Error)。 - 如果是檢查型異常(如
IOException),需要在方法上聲明throws,或手動(dòng)拋RuntimeException包裝。
五、總結(jié):全局異常處理的學(xué)習(xí)路線
- 基礎(chǔ)用法:用
@RestControllerAdvice+@ExceptionHandler捕獲所有異常,返回統(tǒng)一Result。 - 業(yè)務(wù)異常:自定義
BusinessException,在Service層拋出,全局處理返回業(yè)務(wù)碼。 - 參數(shù)校驗(yàn):用
@Valid+MethodArgumentNotValidException,提取校驗(yàn)錯(cuò)誤信息。 - 系統(tǒng)異常:兜底處理
Exception,記錄日志并返回友好提示。 - 進(jìn)階優(yōu)化:自定義狀態(tài)碼、記錄請(qǐng)求上下文、處理表單參數(shù)異常。
掌握這些,你的Spring Boot項(xiàng)目異常處理將告別“屎山代碼”,變得簡(jiǎn)潔、優(yōu)雅、易維護(hù)!趕緊動(dòng)手試試吧~ 有其他問題歡迎在評(píng)論區(qū)留言,我會(huì)一一解答! ??
到此這篇關(guān)于Spring Boot全局異常處理保姆級(jí)教程從入門到實(shí)戰(zhàn)(看完秒懂)的文章就介紹到這了,更多相關(guān)Spring Boot全局異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springBoot項(xiàng)目中的全局異常處理和自定義異常處理實(shí)現(xiàn)
- SpringBoot?實(shí)現(xiàn)全局異常處理的示例代碼
- Springboot?過濾器、攔截器、全局異常處理的方案處理小結(jié)
- SpringBoot自定義全局異常處理器的問題總結(jié)
- SpringBoot中@RestControllerAdvice注解實(shí)現(xiàn)全局異常處理類
- SpringBoot的@ControllerAdvice處理全局異常詳解
- SpringBoot配置全局異常處理器捕獲異常詳解
- SpringBoot實(shí)現(xiàn)全局異常處理方法總結(jié)
- Springboot之自定義全局異常處理的實(shí)現(xiàn)
- SpringBoot如何優(yōu)雅的處理全局異常
相關(guān)文章
SpringBoot導(dǎo)入mail依賴報(bào)錯(cuò)問題解決
這篇文章主要介紹了SpringBoot導(dǎo)入mail依賴報(bào)錯(cuò)問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
springboot配置數(shù)據(jù)庫密碼特殊字符報(bào)錯(cuò)的解決
這篇文章主要介紹了springboot配置數(shù)據(jù)庫密碼特殊字符報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
Java使用junit框架進(jìn)行代碼測(cè)試過程詳解
單元測(cè)試就是針對(duì)最小的功能單元編寫測(cè)試代碼,Junit是使用Java語言實(shí)現(xiàn)的單元測(cè)試框架,它是開源的,Java開發(fā)者都應(yīng)當(dāng)學(xué)習(xí)并使用Junit編寫單元測(cè)試。本文就來講講Junit框架的使用教程,需要的可以參考一下2023-02-02
詳解Javascript判斷Crontab表達(dá)式是否合法
這篇文章主要介紹了詳解Javascript判斷Crontab表達(dá)式是否合法的相關(guān)資料,需要的朋友可以參考下2017-03-03
Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼
這篇文章主要介紹了Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
簡(jiǎn)單了解spring cloud 網(wǎng)關(guān)服務(wù)
這篇文章主要介紹了簡(jiǎn)單了解spring cloud 網(wǎng)關(guān)服務(wù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
使用mybatis的typeHandler對(duì)clob進(jìn)行流讀寫方式
這篇文章主要介紹了使用mybatis的typeHandler對(duì)clob進(jìn)行流讀寫方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

