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

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

如果是ajax請求在做web層時,把返回的jsp變成json格式或者流格式輸出即可,不影響異常框架。如采用struts2結(jié)構(gòu)的代碼:
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; } |
這樣前臺就能根據(jù)我們的異常顯示對應(yīng)的錯誤頁面了,并能把系統(tǒng)知道的和未知的異常記入log。
針對struts2還有個問題,在開發(fā)模式時,struts2和webwork的異常打印在頁面,我們可以根據(jù)頁面輸出進行調(diào)試。一但部署在生產(chǎn)環(huán)境,需要將這個模式關(guān)閉,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序列化錯誤碼的機制,不用傳輸整個異常鏈節(jié)省帶寬。
3、集中l(wèi)ogger服務(wù)處理,所有機器的log統(tǒng)一發(fā)送到集中服務(wù)器處理。logger框架和log4j也有服務(wù)器的機制。
針對web框架、分布式部署、log服務(wù)器再討論:)