From ImportNew:https://mp.weixin.qq.com/s/R3oah9CBiN33Pc5xLEGbrg
這篇文章就是一直以來我想要的異常處理方面的方法
合理地使用異常處理可以幫你節省數小時(甚至數天)調試時間。一個乘法異常會毀掉你的晚餐乃至週末計劃。如果處置不及時,甚至對你的名譽都會造成影響。一個清晰的異常處理策略可以助你節省診斷、重現和問題糾正時間。下面是6條異常處理建議。
1. 使用一個系統全局異常類
不必爲每種異常類型建立單獨的類,一個就夠了。確保這個異常類繼承RuntimeException,這樣可以減少類個數並且移除不必要的異常聲明。
我知道你正在想什麼:如果類型只有一個,那麼怎麼能知道異常具體是什麼?我將如何追蹤具體的屬性?請繼續閱讀。
2. 使用枚舉錯誤碼
我們大多被教授的方法是將異常轉爲錯誤信息。這次查看日誌文件時很好,(呃)但是這樣也有缺點:
-
錯誤信息不會被翻譯(除非你是Google)
-
錯誤信息不會轉換爲用戶友好的文字
-
錯誤信息不能用編程的方式檢測
將異常消息留給開發者定義也會出現同樣的錯誤有多種不同的描述。
一個更好的辦法是使用枚舉表示異常類型。爲每個錯誤分類創建一個枚舉(付款、認證等),讓枚舉實現ErrorCode接口並作爲異常的一個屬性。
當拋出異常時,只要傳入合適的枚舉就可以了。
throw new SystemException(PaymentCode.CREDIT_CARD_EXPIRED);
現在如果需要測試異常只要比較異常代碼和枚舉就可以了。
catch (SystemException e) {
if (e.getErrorCode() == PaymentCode.CREDIT_CARD_EXPIRED) {
...
}
}
通過將錯誤碼作爲查找資源的key就可以方便地提供友好的國際化文本。
public class SystemExceptionExample3 {
public static void main(String[] args) {
System.out.println(getUserText(ValidationCode.VALUE_TOO_SHORT));
}
public static String getUserText(ErrorCode errorCode) {
if (errorCode == null) {
return null;
}
String key = errorCode.getClass().getSimpleName() + "__" + errorCode;
ResourceBundle bundle = ResourceBundle.getBundle("com.northconcepts.exception.example.exceptions");
return bundle.getString(key);
}
}
3. 爲枚舉添加錯誤值
在很多時候可以爲異常添加錯誤值,比如HTTP返回值。這種情況下,可以在ErrorCode接口添加一個getNumber方法並在每個枚舉中實現這個方法。
public enum PaymentCode implements ErrorCode {
SERVICE_TIMEOUT(101),
CREDIT_CARD_EXPIRED(102),
AMOUNT_TOO_HIGH(103),
INSUFFICIENT_FUNDS(104);
private final int number;
private PaymentCode(int number) {
this.number = number;
}
@Override
public int getNumber() {
return number;
}
}
添加錯誤碼可以是全局數值也可以每個枚舉自己負責。你可以直接使用枚舉裏的ordinal()方法或者從文件或數據庫加載。
4. 爲異常添加動態屬性
好的異常處理還應該記錄相關數據而不僅僅是堆棧信息,這樣可以在診斷錯誤和重現錯誤時節省大量時間。用戶不會在你的應用停止工作時告訴你他們到底做了什麼。
最簡單的辦法是給異常添加一個java.util.Map字段。新字段的職責就是通過名字保存相關數據。通過添加setter方法可以遵循流式接口。
可以像下面示例這樣添加相關數據並拋出異常:
throw new SystemException(ValidationCode.VALUE_TOO_SHORT)
.set("field", field)
.set("value", value)
.set("min-length", MIN_LENGTH);
5. 避免不必要的嵌套
冗長的堆棧信息不會有任何幫助,更糟糕的是會浪費你的時間和資源。重新拋出異常時調用靜態函數而不是異常構造函數。封裝的靜態函數決定什麼時候嵌套異常什麼時候只要返回原來的實例。
public static SystemException wrap(Throwable exception, ErrorCode errorCode) {
if (exception instanceof SystemException) {
SystemException se = (SystemException)exception;
if (errorCode != null && errorCode != se.getErrorCode()) {
return new SystemException(exception.getMessage(), exception, errorCode);
}
return se;
} else {
return new SystemException(exception.getMessage(), exception, errorCode);
}
}
public static SystemException wrap(Throwable exception) {
return wrap(exception, null);
}
Your new code for rethrowing exceptions will look like the following.
catch (IOException e) {
throw SystemException.wrap(e).set("fileName", fileName);
}
6. 使用帶Web支持的集中式logger
再額外附贈一個建議。可能你情況很難向產品記錄日誌,這個麻煩可能來自多箇中間商(很多開發者不能直接訪問產品環境)。
在多服務器環境下情況可能會更糟。找到正確的服務器或者確定問題影響到了哪個服務器是一件非常令人頭痛的事情。
我的建議是:
-
將你的日誌記錄到一個地方,推薦記錄到數據庫中。
-
通過Web瀏覽器訪問數據庫。
有很多方法和備選產品可以達成這一目標,log collector、遠程logger、JMX agent、系統監視軟件等。甚至可以自己寫一個。重要的是要快速行動,一旦你達成了目標,你就可以:
-
幾秒鐘之內定位錯誤
-
爲每個異常增加一個URL,可以記錄或者發送email
-
讓你的夥伴可以在沒有你的情況下定位錯誤原因
-
避免測試人員爲同一個bug添加多個記錄。他們可以在bug記錄裏增加一條異常URL
-
省錢
-
讓你的週末和名譽不受影響