處理Java異常的不定法則

由於天天寫bug,代碼裏各個地方充斥着對異常的處理,有時候思路被卡住:這個地方是該拋異常呢,還是我就地解決?是拋出受檢異常(Exception)還是拋出非受檢異常(RuntimeException)呢?這個異常帶不帶狀態碼啊?常常在想,有沒有什麼好的定律可以指導我優雅地處理Java異常。

目前項目中常見的拋異常的做法是

public interface IExceptionCode {
    String name();
    String getMessage();
}

public enum AccountExceptionEnum implements IExceptionCode {
    // 枚舉名就是狀態碼
    ACCOUNT0001("驗證碼錯誤");

    private String message;

    AccountExceptionEnum(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

public class ServiceException extends Exception {
    private String code;

    public ServiceException(String message) {
        super(message);
    }

    public ServiceException(String code, String message) {
        this(message);
        this.setCode(code);
    }

    public ServiceException(String code, String message, Throwable cause) {
        super(message, cause);
        this.setCode(code);
    }

    public ServiceException(String code, Throwable cause) {
        super(cause);
        this.setCode(code);
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getCode() {
        return this.code;
    }
}

// 定義特定異常
public class AccountServiceException extends ServiceException {
    public MarsServiceException(String message) {
        super(message);
    }

    public MarsServiceException(IExceptionCode message) {
        super(message.getMessage());
        super.setCode(message.name());
    }
}

拋出帶狀態碼的特定受檢異常:

throw new AccountServiceException(AccountExceptionEnum.ACCOUNT0001);

這些特定ServiceException類型的異常都拋到了前端,異常序列化後的字符串:

{
    "code": "ACCOUNT0001",
    "message": "驗證碼錯誤",
    "data": null
}

每次要拋出一個業務異常的時候,都要寫一個code和message,當業務流程校驗比較複雜的時候,就要在AccountExceptionEnum裏定義很多的code、message。感覺不爽的是,這大量的code碼沒有產生業務意義,覺得很冗餘。還有項目中異常的使用也比較亂。

那來給寄幾定義幾個使用異常的不定法則吧,從此寄幾再也不用擔心我使用異常了。

一、業務性api、open api、soa接口層拋出特定的受檢(Exception)異常,不拋非受檢異常(RuntimeException)

api接口和soa接口聲明受檢異常,拋出特定受檢異常(比如上面定義的AccountServiceException),這樣controller層對異常的序列化也比較方便,清晰明瞭,就告訴你我會拋出這種類型的異常,你小心點。

二、狀態碼有業務意義,才拋出帶狀態碼的異常
1. 開放平臺的open api接口

開放平臺的open api接口產生的異常都要帶上狀態碼,以便第三方平臺調用的識別。

2、一般業務系統api接口

將項目中的異常分成兩類,有狀態碼和沒有狀態碼。將狀態碼拋出去一定得是有業務意義的,不然就直接將錯誤message拋給前端就好了。秉承的原則就是不要寫一點多餘的代碼。

3、soa接口

服務之間的調用,原則和2中所說一樣。

4、業務方法

只拋帶錯誤message的異常。

三、以fail fast爲原則,需要fail fast情景拋出非受檢異常(RuntimeException);其他情況拋出受檢異常(Exception)

使用guava的Preconditions(checkNotNull、checkArgument……)或jsr 303(@NotNull、@NotBlank、@NotEmpty……)這種類似校驗都拋出非受檢異常(RuntimeException)。這些校驗符合fail fast原則,你要不滿足這些條件我就馬上讓你失敗,啥也撈不到,並且也不告訴你要拋出什麼類型的異常。剩下的就是業務邏輯的校驗,拋出受檢異常。

在目前的工作過程中,我總結出這三個不定法則,覺得這樣做會少些麻煩、方便處理、少寫代碼,有了規則,讓項目的後續開發以及維護都變得比較便利了。

如有不妥,歡迎拍磚。

參考文章:
如何優雅的設計 Java 異常
Java異常(二) 《Effective Java》中關於異常處理的幾條建議

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章