SpringBoot統一異常處理

丿似锦發表於2024-08-15

簡介

Spring Boot專案中實現統一的異常處理是一種常見的做法,這有助於保持程式碼的整潔並提供一致的錯誤響應格式。Spring Boot 中的統一異常處理是一種機制,用於集中管理和格式化應用程式中丟擲的所有異常。這種機制可以提高程式的健壯性和使用者體驗,同時簡化開發過程。

統一異常處理的一些主要作用:

  1. 一致性:
    保證了在不同地方發生的異常都能以相同的方式被處理和響應。
    使用者或客戶端接收到的錯誤資訊和格式保持一致,有助於問題定位。
  2. 錯誤資訊定製:
    可以根據需要定製錯誤資訊,包括HTTP狀態碼、錯誤程式碼和描述等。
    這樣可以更好地向使用者解釋發生了什麼問題,並提供可能的解決方法。
  3. 日誌記錄:
    在捕獲異常後,可以根據異常型別和級別記錄到日誌檔案中,便於後續分析和除錯。
    日誌記錄可以幫助追蹤問題發生的時間點、環境和上下文。
  4. 資源釋放:
    在某些情況下,可以在異常處理過程中釋放資源或者進行清理工作,確保系統穩定執行。
  5. API 文件化:
    對於對外提供的 API,可以透過異常處理來定義預期的錯誤情況,這有助於生成清晰的 API 文件。
  6. 安全性:
    可以避免敏感資訊洩露給客戶端,例如資料庫查詢語句、內部類名等。
    可以返回更泛化的錯誤資訊,而不是具體的異常堆疊跟蹤。
  7. 效能最佳化:
    透過合理設計異常處理邏輯,可以減少不必要的資源消耗和呼叫鏈路,從而提升整體效能。

實現

1.建立自定義異常類

根據專案場景建立一些自定義異常類 extends RuntimeException

  • WarnException 警告類異常
@Data
public class WarnException extends RuntimeException{

    public WarnException(String msg){
        super(msg);
    }
}

返回告警資訊,
日誌記錄WARN級別日誌,且不列印堆疊
異常概要資訊不會落庫

  • UserException 使用者業務類異常
@Data
public class UserException extends RuntimeException {

    public UserException(String msg) {
        super(msg);
    }

    @Override
    public String toString() {
        return getClass().getName() + StringConstant.COLON +this.getMessage();
    }
}

返回告警資訊,
日誌記錄ERROR級別日誌,且列印堆疊
異常概要資訊落庫

StringConstant.COLON = ":"

  • PlatformException 系統內部異常
@Data
public class PlatformException extends RuntimeException {

    public PlatformException(String msg) {
        super(msg);
    }

    @Override
    public String toString() {
        return getClass().getName() + StringConstant.COLON +this.getMessage();
    }

}

返回告警資訊,
日誌記錄ERROR級別日誌,且列印堆疊
異常概要資訊落庫

StringConstant.COLON = ":"

2.建立全域性異常處理器

定義一個帶有 @ControllerAdvice 註解的類,並在其中宣告一個或多個帶有 @ExceptionHandler 註解的方法來處理特定型別的異常

  • ZKExceptionHandler統一異常處理類
@ControllerAdvice
@Slf4j
public class ZKExceptionHandler {

    @Autowired
    private AlarmMsgService alarmMsgService;

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public <T> MsgData<T> handleException(Exception e) {
        //預期內告警類異常不計入資料庫;日誌不記錄堆疊
        if (e instanceof WarnException) {
            WarnException warnException = (WarnException) e;
            log.warn(warnException.getMessage());
            return MsgData.fill(warnException.getMessage());
        }
        String msg = e.getMessage();
        String exceptionName = e.getClass().getName();
        String stackTrace = ExceptionUtils.getStackTrace(e);
        //使用者異常
        if (e instanceof UserException) {
            alarmMsgService.saveUserAlarmMsg(msg, AlarmLevelEnum.ALARM, exceptionName, stackTrace);
            log.error("UserException", e);
            return MsgData.fill(msg);
        }
        //平臺類異常
        if (e instanceof PlatformException) {
            alarmMsgService.savePlatformAlarmMsg(msg, AlarmLevelEnum.ALARM, exceptionName, stackTrace);
            log.error("PlatformException", e);
            return MsgData.fill(msg);
        }
        //其他異常
        alarmMsgService.saveOtherAlarmMsg("系統異常", AlarmLevelEnum.ALARM, exceptionName, stackTrace);
        log.error("OtherException", e);
        return MsgData.fill(msg);
    }
}

alarmMsgService為異常概要資訊入庫操作,可自定義替換為自己的資料庫操作
這裡會對三種自定義異常分別進行處理,用於達到資訊返回及記錄預期
對於其他異常也會進行兜底記錄及處理

  • MsgData 返回實體類
@Data
public class MsgData<T> {
    private int status;
    private String errorCode;
    private String msg;
    private T data;

    public static <T> MsgData<T> success() {
        return success(null);
    }

    public static <T> MsgData<T> success(T data) {
        return success(null, data);
    }

    public static <T> MsgData<T> success(String msg) {
        return success(msg, null);
    }

    public static <T> MsgData<T> success(String msg, T data) {
        return success(HttpCodeConstant.SUCCESS, msg, data);
    }

    public static <T> MsgData<T> success(int status, String msg, T data) {
        MsgData<T> msgData = new MsgData<>();
        msgData.setStatus(status);
        msgData.setMsg(msg);
        msgData.setData(data);
        return msgData;
    }

    public static <T> MsgData<T> fill(String msg) {
        return fill(null, msg);
    }

    public static <T> MsgData<T> fill(String errorCode, String msg) {
        return fill(HttpCodeConstant.ERROR, errorCode, msg, null);
    }

    public static <T> MsgData<T> fill(int status, String errorCode, String msg, T data) {
        MsgData<T> msgData = new MsgData<>();
        msgData.setStatus(status);
        msgData.setErrorCode(errorCode);
        msgData.setMsg(msg);
        msgData.setData(data);
        return msgData;
    }

}

HttpCodeConstant.SUCCESS = 200
HttpCodeConstant.ERROR = 500

使用

透過throw 不同的Exception進行區分不同的異常

// 警告類異常
throw new WarnException("測試WARN告警");
// 使用者業務類異常
throw new UserException("測試使用者告警");
// 平臺系統類異常
throw new PlatformException("測試平臺告警");
// 其他異常
throw new RuntimeException("測試系統告警");

結束

相關文章