欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Boot全局異常處理實(shí)戰(zhàn)指南

 更新時(shí)間:2025年09月03日 08:46:39   作者:白侖色  
在開(kāi)發(fā)過(guò)程中,異常是不可避免的,Spring?Boot提供了一種簡(jiǎn)單而強(qiáng)大的機(jī)制來(lái)處理應(yīng)用程序中的異常,即全局異常處理,這篇文章主要介紹了Spring?Boot全局異常處理的相關(guān)資料,需要的朋友可以參考下

引言

在開(kāi)發(fā) Spring Boot 項(xiàng)目時(shí),你是否遇到過(guò)這些問(wèn)題?

  • ? 不同 Controller 重復(fù)寫(xiě) try-catch
  • ? 異常信息格式不統(tǒng)一,前端難以解析
  • ? 系統(tǒng)內(nèi)部錯(cuò)誤直接暴露給用戶(hù)(如堆棧信息)
  • ? 404、500 等狀態(tài)碼處理混亂

這些問(wèn)題不僅影響用戶(hù)體驗(yàn),還可能帶來(lái)安全風(fēng)險(xiǎn)。

全局異常處理就是 Spring Boot 為我們提供的“統(tǒng)一調(diào)度中心”,它能在異常發(fā)生時(shí)自動(dòng)攔截、處理,并返回友好的響應(yīng)。

本文將帶你從零開(kāi)始,構(gòu)建一套生產(chǎn)級(jí)的全局異常處理機(jī)制,并結(jié)合實(shí)際代碼,讓你徹底掌握這一核心技能。

一、為什么需要全局異常處理?

想象一個(gè)電商系統(tǒng):

  • 用戶(hù)下單時(shí)庫(kù)存不足 → 返回“庫(kù)存不足,請(qǐng)稍后再試”
  • 訂單ID格式錯(cuò)誤 → 返回“訂單不存在”
  • 數(shù)據(jù)庫(kù)連接失敗 → 記錄日志,返回“系統(tǒng)繁忙,請(qǐng)稍后重試”

如果沒(méi)有統(tǒng)一處理,每個(gè)接口都要寫(xiě)類(lèi)似的 try-catch,代碼重復(fù)且難以維護(hù)。

而有了全局異常處理,就像設(shè)立了一個(gè)“客戶(hù)服務(wù)中心”,所有異常都由它統(tǒng)一接待、分類(lèi)處理、禮貌回應(yīng)。

二、核心技術(shù):@ControllerAdvice和@ExceptionHandler

Spring Boot 提供了兩個(gè)核心注解來(lái)實(shí)現(xiàn)全局異常處理:

注解作用
@ControllerAdvice定義全局異常處理器(可作用于所有 Controller)
@ExceptionHandler指定處理某類(lèi)異常的方法

三、實(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ù)異?;?lèi)
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, "購(gòu)買(mǎi)數(shù)量必須大于0");
        }
        // ... 其他邏輯
    }
}

3.3 全局異常處理器(核心)

@RestControllerAdvice  // 等價(jià)于 @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) {
        // 獲取第一個(gè)錯(cuò)誤信息
        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)部錯(cuò)誤,請(qǐng)聯(lián)系管理員");
    }

    /**
     * 處理所有未被捕獲的異常(兜底)
     */
    @ExceptionHandler(Exception.class)
    public ApiResponse<String> handleException(Exception e) {
        log.error("未處理異常", e);
        return ApiResponse.error(500, "系統(tǒng)繁忙,請(qǐng)稍后重試");
    }

    /**
     * 處理 404 Not Found
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResponse<String> handle404(NoHandlerFoundException e) {
        log.warn("請(qǐng)求路徑不存在: {}", e.getRequestURL());
        return ApiResponse.error(404, "請(qǐng)求的資源不存在");
    }
}

3.4 啟用 404 異常捕獲(重要?。?/h3>

默認(rèn)情況下,404 異常不會(huì)進(jìn)入 @ExceptionHandler,需在配置文件中開(kāi)啟:

# application.yml
spring:
  mvc:
    throw-exception-if-no-handler-found: true  # 找不到處理器時(shí)拋出異常

  web:
    resources:
      add-mappings: false  # 關(guān)閉默認(rèn)靜態(tài)資源映射(可選,更嚴(yán)格)

四、測(cè)試驗(yàn)證

4.1 創(chuàng)建測(cè)試 Controller

@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/business")
    public ApiResponse<String> businessError() {
        throw new BusinessException(400, "用戶(hù)名已存在");
    }

    @PostMapping("/validate")
    public ApiResponse<String> validate(@Valid @RequestBody UserForm form) {
        return ApiResponse.success("驗(yàn)證通過(guò)");
    }

    @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("未知錯(cuò)誤");
    }
}
class UserForm {
    @NotBlank(message = "用戶(hù)名不能為空")
    private String username;

    @Min(value = 18, message = "年齡不能小于18歲")
    private Integer age;

    // getter & setter
}

4.2 測(cè)試結(jié)果

請(qǐng)求響應(yīng)
GET /api/business{"code":400,"message":"用戶(hù)名已存在","data":null}
POST /api/validate(無(wú)參數(shù)){"code":400,"message":"用戶(hù)名不能為空","data":null}
GET /api/null{"code":500,"message":"系統(tǒng)內(nèi)部錯(cuò)誤,請(qǐng)聯(lián)系管理員","data":null}
GET /api/unknown{"code":500,"message":"系統(tǒng)繁忙,請(qǐng)稍后重試","data":null}
GET /api/not-exist{"code":404,"message":"請(qǐng)求的資源不存在","data":null}

五、高級(jí)技巧與生產(chǎn)實(shí)踐

5.1 異常分類(lèi)管理

你可以為不同模塊定義不同的異常處理器:

// 用戶(hù)模塊異常處理器
@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 返回錯(cuò)誤碼枚舉(推薦)

public enum ErrorCode {
    SUCCESS(200, "成功"),
    BAD_REQUEST(400, "請(qǐng)求參數(shù)錯(cuò)誤"),
    UNAUTHORIZED(401, "未授權(quán)"),
    FORBIDDEN(403, "禁止訪(fǎng)問(wèn)"),
    NOT_FOUND(404, "資源不存在"),
    SERVER_ERROR(500, "系統(tǒng)內(nèi)部錯(cuò)誤");

    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í)踐說(shuō)明
? 使用 @RestControllerAdvice統(tǒng)一返回 JSON 格式
? 自定義 BusinessException區(qū)分業(yè)務(wù)異常與系統(tǒng)異常
? 記錄日志@Slf4j + log.error/warn
? 敏感信息脫敏不要將數(shù)據(jù)庫(kù)錯(cuò)誤、堆棧信息暴露給前端
? 合理分類(lèi)異常優(yōu)先處理具體異常,最后是 Exception
? 啟用 404 捕獲配置 throw-exception-if-no-handler-found: true

到此這篇關(guān)于Spring Boot全局異常處理的文章就介紹到這了,更多相關(guān)SpringBoot全局異常處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論