首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 自定義異常設計最佳實踐:從業務場景到代碼落地完整指南

     編輯:Java 相關知識

為什麼需要自定義異常?

場景說明
統一對外錯誤碼微服務間、前後端分離約定統一 code/message 格式
業務語義表達如 InsufficientBalanceException 比 RuntimeException 更清晰
隱藏底層細節封裝 SQL/網絡異常,避免洩露堆棧敏感信息
快速定位異常類名即業務含義,日誌檢索效率高
控制流終止校驗失敗直接 throw,配合 @ControllerAdvice 統一響應

自定義異常體系設計

基礎異常類

public class BizException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    
    private final String errorCode;      // 業務錯誤碼
    private final boolean i18nKey;       // message 是否為 i18n key
    
    public BizException(String errorCode, String message) {
        this(errorCode, message, true, null);
    }
    
    public BizException(String errorCode, String message, Throwable cause) {
        this(errorCode, message, true, cause);
    }
    
    public BizException(String errorCode, String message, boolean i18nKey, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
        this.i18nKey = i18nKey;
    }
    // getters...
}

具體業務異常(枚舉式管理)

public enum ErrorCode {
    USER_NOT_FOUND("USER_001", "用戶不存在"),
    INSUFFICIENT_BALANCE("PAY_002", "餘額不足"),
    PARAM_INVALID("SYS_003", "參數校驗失敗: {0}");
    
    private final String code, template;
    ErrorCode(String code, String template) { this.code=code; this.template=template; }
    
    public BizException newException(Object... args) {
        String msg = MessageFormat.format(template, args);
        return new BizException(code, msg);
    }
}

// 使用
throw ErrorCode.USER_NOT_FOUND.newException(userId);
throw ErrorCode.PARAM_INVALID.newException("email");

Spring MVC 統一異常處理

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
    @ExceptionHandler(BizException.class)
    public R handleBiz(BizException e) {
        log.warn("業務異常: {} - {}", e.getErrorCode(), e.getMessage());
        return R.error(e.getErrorCode(), e.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R handleValid(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldErrors().stream()
            .map(fe -> fe.getField() + ": " + fe.getDefaultMessage())
            .collect(Collectors.joining("; "));
        return R.error("SYS_003", msg);
    }
    
    @ExceptionHandler(Exception.class)
    public R handleSys(Exception e) {
        log.error("系統異常", e);
        return R.error("SYS_999", "系統繁忙,請稍後重試");
    }
}

測試驗證

@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        if (id <= 0) throw ErrorCode.PARAM_INVALID.newException("id");
        User user = userService.findById(id);
        if (user == null) throw ErrorCode.USER_NOT_FOUND.newException(id);
        return user;
    }
}

避坑指南

  1. 異常濫用:非異常流程(如分頁無數據)勿用異常,返回空集合/Optional
  2. 丟失堆棧:throw new BizException(...) 而非 throw new BizException(e.getMessage()),保留 cause
  3. 過度繼承:業務異常扁平化,避免深層繼承樹
  4. 國際化:message 存 i18n key,前端按 locale 渲染
  5. 性能:高頻場景(如參數校驗)預構建異常實例或用錯誤碼枚舉

完整代碼結構

com.example.exception
├── BizException.java           // 基礎異常
├── ErrorCode.java              // 錯誤碼枚舉
├── GlobalExceptionHandler.java // 統一處理
└── R.java                      // 統一響應封裝
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved