不管是什么程序開發都可能會出現各種各樣的異常。可能是程序錯誤,也可能是業務邏輯錯誤。針對這個各個開發人員都有自己的處理方式,不同的風格增加了業務系統的復雜度和維護難度。所以定義好一個統一的異常處理框架還是需要的。我們開發框架采用java實現,java中的異常一般分為兩種,檢查異常和運行時異常。檢查異常(checked exception)有可能是程序的業務異常,這種異常一般都是開發人員自定義的、知道什么時候會拋出什么異常并進行捕捉處理。也可以是系統的異常,不捕捉編譯不會通過,如 IOException、SQLException、ClassNotFoundException , 這種是必須要捕捉的并且大多都是繼承Exception。 運行時異常一般都是系統拋出來的異常,這種異常不捕捉處理也不會報編譯錯誤,如NullPointerException,ClassCastException。運行異常都是繼承至RuntimeException。不管是檢查異常還是運行時異常都是繼承至Exception。另外還有一種異常是系統錯誤Error,這種異常是系統出現了故障拋出來的不能捕捉,如OutOfMemoryError。Exception和Error都是繼承至Throwable。
了解了java的異常體系后,我們設計一下web框架的異常處理格式。在以往EJB時代的J2ee系統,一般是標準的三層架構:web層、業務邏輯層、數據訪問層,并且每一層都分別部署在不同的機器集群中。這樣我們的異常一般分為三個,WebException、BizException、DAOException分別映射到web層、業務邏輯層、數據訪問層。并且這些異常都要設計的串行化可以跨機器傳遞生成異常鏈。這樣的好處是看到異常鏈知道從哪兒拋出來的錯誤,比較清晰明了。

隨著后面spring的推出,java的開發越來越輕量級很多時候一臺服務器可以同時部署三層并集群化,架構模式也慢慢由充血模式演變為貧血模式,再也沒有了厚重的實體Bean和有狀態會話Bean。針對現在輕量級的框架,異常結構如何設計呢?
先看看我們這個異常結構需要解決的問題是什么?
1、規范大家的異常處理方式。
2、簡化異常處理。
3、區分業務異常和系統異常,業務異常需要業務邏輯支持,系統異常需要記錄log。
4、友好的異常展示。
5、異常結構可擴展。
針對這些點,我的想法是開發可以使用三個類:SDKException、BizException、BizSystemException。SDKException是處理的基礎類,可以在里面封裝一些異常處理的基本函數。BizException是業務邏輯處理異常,一般這類異常是不需要記錄log,只是展示給頁面顯示并提示給用戶。如你的用戶名、密碼為空等錯誤。BizSystemException是業務系統異常,這類異常一般需要捕捉并記錄log,比如數據庫的主鍵沖突、sql語句錯誤等。按照三層架構的話,我們不可能對每一層都捕捉并且記錄log,會造成重復log。可以從DAO層把捕捉到的數據庫異常轉換為BizSystemException拋出,如果有BizException也拋出。業務邏輯層對于BizSystemException、BizException不處理直接拋出。所有處理都在web層進行集中處理,如果是BizException,根據錯誤碼和錯誤消息顯示給用戶對應的頁面和錯誤消息。如果是BizSystemException告知用戶系統錯誤,并把錯誤結果記入log。如果是其他異常和BizSystemException一致。這樣就減少了異常處理的復雜度,開發也不用關心什么檢查異常,運行時異常。

如果是ajax請求在做web層時,把返回的jsp變成json格式或者流格式輸出即可,不影響異常框架。如采用struts2結構的代碼:
public String testAjax() { try { genAjaxDataStr(0, "{}"); } catch (BizException e) { getRequest().setAttribute(this.ERRORMESSAGE, e.getErrorMessage()); return this.ERRORJSON; } catch (BizSystemException e) { getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR); return this.ERRORJSON; } catch (Exception e) { this.errorTrace("test", e.getMessage(), e); getRequest().setAttribute(this.ERRORMESSAGE, this.SYSTEMERROR); return this.ERRORJSON; } return this.NONE; } |
這樣前臺就能根據我們的異常顯示對應的錯誤頁面了,并能把系統知道的和未知的異常記入log。
針對struts2還有個問題,在開發模式時,struts2和webwork的異常打印在頁面,我們可以根據頁面輸出進行調試。一但部署在生產環境,需要將這個模式關閉,log就沒有了。
<constant name="struts.devMode" value="false" /> |
為了記錄一些未知的錯誤,需要做以下步驟:
1、將全局的異常映射頁面從struts2的包定義里去掉。如果不去掉,在webwork不會拋出異常也就找不到出了什么問題。
2、擴展struts的DispatcherFilter捕捉未知的異常并記錄入log。
<global-exception-mappings> <exception-mapping exception="com.linktong.sdk.biz.exception.BizException" result="checkedException" /> </global-exception-mappings> |
這樣,基本的異常框架就搭建完成。更進一步需要做的是:
1、分布式全局錯誤碼體系,保證所有機器都共用一套錯誤碼。
2、分布式部署,異常傳遞。可以采用hessian序列化錯誤碼的機制,不用傳輸整個異常鏈節省帶寬。
3、集中logger服務處理,所有機器的log統一發送到集中服務器處理。logger框架和log4j也有服務器的機制。
針對web框架、分布式部署、log服務器再討論:)