Spring?Boot全局異常處理實(shí)戰(zhàn)指南
引言
在開發(fā) Spring Boot 項(xiàng)目時,你是否遇到過這些問題?
- ? 不同 Controller 重復(fù)寫
try-catch - ? 異常信息格式不統(tǒng)一,前端難以解析
- ? 系統(tǒng)內(nèi)部錯誤直接暴露給用戶(如堆棧信息)
- ? 404、500 等狀態(tài)碼處理混亂
這些問題不僅影響用戶體驗(yàn),還可能帶來安全風(fēng)險。
全局異常處理就是 Spring Boot 為我們提供的“統(tǒng)一調(diào)度中心”,它能在異常發(fā)生時自動攔截、處理,并返回友好的響應(yīng)。
本文將帶你從零開始,構(gòu)建一套生產(chǎn)級的全局異常處理機(jī)制,并結(jié)合實(shí)際代碼,讓你徹底掌握這一核心技能。
一、為什么需要全局異常處理?
想象一個電商系統(tǒng):
- 用戶下單時庫存不足 → 返回“庫存不足,請稍后再試”
- 訂單ID格式錯誤 → 返回“訂單不存在”
- 數(shù)據(jù)庫連接失敗 → 記錄日志,返回“系統(tǒng)繁忙,請稍后重試”
如果沒有統(tǒng)一處理,每個接口都要寫類似的 try-catch,代碼重復(fù)且難以維護(hù)。
而有了全局異常處理,就像設(shè)立了一個“客戶服務(wù)中心”,所有異常都由它統(tǒng)一接待、分類處理、禮貌回應(yīng)。
二、核心技術(shù):@ControllerAdvice和@ExceptionHandler
Spring Boot 提供了兩個核心注解來實(shí)現(xiàn)全局異常處理:
| 注解 | 作用 |
|---|---|
@ControllerAdvice | 定義全局異常處理器(可作用于所有 Controller) |
@ExceptionHandler | 指定處理某類異常的方法 |
三、實(shí)戰(zhàn):構(gòu)建統(tǒng)一異常處理機(jī)制
3.1 定義統(tǒng)一返回格式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ApiResponse<T> {
private int code;
private String message;
private T data;
// 成功響應(yīng)
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
// 失敗響應(yīng)
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
3.2 自定義業(yè)務(wù)異常
// 業(yè)務(wù)異?;?
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(String message) {
super(message);
this.code = 500;
}
// getter
public int getCode() {
return code;
}
}
使用示例:
@Service
public class OrderService {
public void createOrder(Long productId, Integer quantity) {
if (quantity <= 0) {
throw new BusinessException(400, "購買數(shù)量必須大于0");
}
// ... 其他邏輯
}
}
3.3 全局異常處理器(核心)
@RestControllerAdvice // 等價于 @ControllerAdvice + @ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 處理自定義業(yè)務(wù)異常
*/
@ExceptionHandler(BusinessException.class)
public ApiResponse<String> handleBusinessException(BusinessException e) {
log.warn("業(yè)務(wù)異常: {}", e.getMessage());
return ApiResponse.error(e.getCode(), e.getMessage());
}
/**
* 處理參數(shù)校驗(yàn)異常(@Valid)
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse<String> handleValidationException(MethodArgumentNotValidException e) {
// 獲取第一個錯誤信息
String errorMessage = e.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.findFirst()
.orElse("參數(shù)校驗(yàn)失敗");
log.warn("參數(shù)校驗(yàn)異常: {}", errorMessage);
return ApiResponse.error(400, errorMessage);
}
/**
* 處理空指針異常
*/
@ExceptionHandler(NullPointerException.class)
public ApiResponse<String> handleNullPointerException(NullPointerException e) {
log.error("空指針異常", e);
return ApiResponse.error(500, "系統(tǒng)內(nèi)部錯誤,請聯(lián)系管理員");
}
/**
* 處理所有未被捕獲的異常(兜底)
*/
@ExceptionHandler(Exception.class)
public ApiResponse<String> handleException(Exception e) {
log.error("未處理異常", e);
return ApiResponse.error(500, "系統(tǒng)繁忙,請稍后重試");
}
/**
* 處理 404 Not Found
*/
@ExceptionHandler(NoHandlerFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ApiResponse<String> handle404(NoHandlerFoundException e) {
log.warn("請求路徑不存在: {}", e.getRequestURL());
return ApiResponse.error(404, "請求的資源不存在");
}
}
3.4 啟用 404 異常捕獲(重要?。?/h3>
默認(rèn)情況下,404 異常不會進(jìn)入 @ExceptionHandler,需在配置文件中開啟:
# application.yml
spring:
mvc:
throw-exception-if-no-handler-found: true # 找不到處理器時拋出異常
web:
resources:
add-mappings: false # 關(guān)閉默認(rèn)靜態(tài)資源映射(可選,更嚴(yán)格)
四、測試驗(yàn)證
4.1 創(chuàng)建測試 Controller
@RestController
@RequestMapping("/api")
public class TestController {
@GetMapping("/business")
public ApiResponse<String> businessError() {
throw new BusinessException(400, "用戶名已存在");
}
@PostMapping("/validate")
public ApiResponse<String> validate(@Valid @RequestBody UserForm form) {
return ApiResponse.success("驗(yàn)證通過");
}
@GetMapping("/null")
public ApiResponse<String> nullPointer() {
String str = null;
str.length(); // 觸發(fā) NullPointerException
return ApiResponse.success("success");
}
@GetMapping("/unknown")
public ApiResponse<String> unknown() {
throw new RuntimeException("未知錯誤");
}
}
class UserForm {
@NotBlank(message = "用戶名不能為空")
private String username;
@Min(value = 18, message = "年齡不能小于18歲")
private Integer age;
// getter & setter
}
4.2 測試結(jié)果
| 請求 | 響應(yīng) |
|---|---|
GET /api/business | {"code":400,"message":"用戶名已存在","data":null} |
POST /api/validate(無參數(shù)) | {"code":400,"message":"用戶名不能為空","data":null} |
GET /api/null | {"code":500,"message":"系統(tǒng)內(nèi)部錯誤,請聯(lián)系管理員","data":null} |
GET /api/unknown | {"code":500,"message":"系統(tǒng)繁忙,請稍后重試","data":null} |
GET /api/not-exist | {"code":404,"message":"請求的資源不存在","data":null} |
五、高級技巧與生產(chǎn)實(shí)踐
5.1 異常分類管理
你可以為不同模塊定義不同的異常處理器:
// 用戶模塊異常處理器
@ControllerAdvice("com.example.controller.user")
public class UserExceptionHandler { ... }
// 訂單模塊異常處理器
@ControllerAdvice("com.example.controller.order")
public class OrderExceptionHandler { ... }
5.2 結(jié)合 AOP 記錄異常日志
@Aspect
@Component
public class ExceptionLogAspect {
@AfterThrowing(pointcut = "@within(org.springframework.web.bind.annotation.RestController)",
throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
String methodName = joinPoint.getSignature().getName();
log.error("方法 {} 發(fā)生異常: {}", methodName, ex.getMessage());
}
}
5.3 返回錯誤碼枚舉(推薦)
public enum ErrorCode {
SUCCESS(200, "成功"),
BAD_REQUEST(400, "請求參數(shù)錯誤"),
UNAUTHORIZED(401, "未授權(quán)"),
FORBIDDEN(403, "禁止訪問"),
NOT_FOUND(404, "資源不存在"),
SERVER_ERROR(500, "系統(tǒng)內(nèi)部錯誤");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
// getter
}
使用:
throw new BusinessException(ErrorCode.BAD_REQUEST);
總結(jié):全局異常處理的最佳實(shí)踐
| 實(shí)踐 | 說明 |
|---|---|
? 使用 @RestControllerAdvice | 統(tǒng)一返回 JSON 格式 |
? 自定義 BusinessException | 區(qū)分業(yè)務(wù)異常與系統(tǒng)異常 |
| ? 記錄日志 | @Slf4j + log.error/warn |
| ? 敏感信息脫敏 | 不要將數(shù)據(jù)庫錯誤、堆棧信息暴露給前端 |
| ? 合理分類異常 | 優(yōu)先處理具體異常,最后是 Exception |
| ? 啟用 404 捕獲 | 配置 throw-exception-if-no-handler-found: true |
到此這篇關(guān)于Spring Boot全局異常處理的文章就介紹到這了,更多相關(guān)SpringBoot全局異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot項(xiàng)目上高并發(fā)問題的解決方案
本章演示在springboot項(xiàng)目中的高并發(fā)demo,演示導(dǎo)致的問題,以及單機(jī)部署下的解決方案和集群部署下的解決方式以及分布式下的解決方案,文中通過圖文結(jié)合的方式講解的非常詳細(xì),需要的朋友可以參考下2024-06-06
Java for循環(huán)常見優(yōu)化方法案例詳解
這篇文章主要介紹了Java for循環(huán)常見優(yōu)化方法案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
深入淺析Random類在高并發(fā)下的缺陷及JUC對其的優(yōu)化
這篇文章主要介紹了Random類在高并發(fā)下的缺陷及JUC對其的優(yōu)化 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04

