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

