JAVA WEB框架的錯(cuò)誤體系
不管是什么程序開發(fā)都可能會(huì)出現(xiàn)各種各樣的異常。可能是程序錯(cuò)誤,也可能是業(yè)務(wù)邏輯錯(cuò)誤。針對這個(gè)各個(gè)開發(fā)人員都有自己的處理方式,不同的風(fēng)格增加了業(yè)務(wù)系統(tǒng)的復(fù)雜度和維護(hù)難度。所以定義好一個(gè)統(tǒng)一的異常處理框架還是需要的。我們開發(fā)框架采用java實(shí)現(xiàn),java中的異常一般分為兩種,檢查異常和運(yùn)行時(shí)異常。檢查異常(checked exception)有可能是程序的業(yè)務(wù)異常,這種異常一般都是開發(fā)人員自定義的、知道什么時(shí)候會(huì)拋出什么異常并進(jìn)行捕捉處理。也可以是系統(tǒng)的異常,不捕捉編譯不會(huì)通過,如 IOException、SQLException、ClassNotFoundException , 這種是必須要捕捉的并且大多都是繼承Exception。 運(yùn)行時(shí)異常一般都是系統(tǒng)拋出來的異常,這種異常不捕捉處理也不會(huì)報(bào)編譯錯(cuò)誤,如NullPointerException,ClassCastException。運(yùn)行異常都是繼承至RuntimeException。不管是檢查異常還是運(yùn)行時(shí)異常都是繼承至Exception。另外還有一種異常是系統(tǒng)錯(cuò)誤Error,這種異常是系統(tǒng)出現(xiàn)了故障拋出來的不能捕捉,如OutOfMemoryError。Exception和Error都是繼承至Throwable。
了解了java的異常體系后,我們設(shè)計(jì)一下web框架的異常處理格式。在以往EJB時(shí)代的J2ee系統(tǒng),一般是標(biāo)準(zhǔn)的三層架構(gòu):web層、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層,并且每一層都分別部署在不同的機(jī)器集群中。這樣我們的異常一般分為三個(gè),WebException、BizException、DAOException分別映射到web層、業(yè)務(wù)邏輯層、數(shù)據(jù)訪問層。并且這些異常都要設(shè)計(jì)的串行化可以跨機(jī)器傳遞生成異常鏈。這樣的好處是看到異常鏈知道從哪兒拋出來的錯(cuò)誤,比較清晰明了。
隨著后面spring的推出,java的開發(fā)越來越輕量級很多時(shí)候一臺服務(wù)器可以同時(shí)部署三層并集群化,架構(gòu)模式也慢慢由充血模式演變?yōu)樨氀J剑僖矝]有了厚重的實(shí)體Bean和有狀態(tài)會(huì)話Bean。針對現(xiàn)在輕量級的框架,異常結(jié)構(gòu)如何設(shè)計(jì)呢?
先看看我們這個(gè)異常結(jié)構(gòu)需要解決的問題是什么?
1、規(guī)范大家的異常處理方式。
2、簡化異常處理。
3、區(qū)分業(yè)務(wù)異常和系統(tǒng)異常,業(yè)務(wù)異常需要業(yè)務(wù)邏輯支持,系統(tǒng)異常需要記錄log。
4、友好的異常展示。
5、異常結(jié)構(gòu)可擴(kuò)展。
針對這些點(diǎn),我的想法是開發(fā)可以使用三個(gè)類:SDKException、BizException、BizSystemException。SDKException是處理的基礎(chǔ)類,可以在里面封裝一些異常處理的基本函數(shù)。BizException是業(yè)務(wù)邏輯處理異常,一般這類異常是不需要記錄log,只是展示給頁面顯示并提示給用戶。如你的用戶名、密碼為空等錯(cuò)誤。BizSystemException是業(yè)務(wù)系統(tǒng)異常,這類異常一般需要捕捉并記錄log,比如數(shù)據(jù)庫的主鍵沖突、sql語句錯(cuò)誤等。按照三層架構(gòu)的話,我們不可能對每一層都捕捉并且記錄log,會(huì)造成重復(fù)log。可以從DAO層把捕捉到的數(shù)據(jù)庫異常轉(zhuǎn)換為BizSystemException拋出,如果有BizException也拋出。業(yè)務(wù)邏輯層對于BizSystemException、BizException不處理直接拋出。所有處理都在web層進(jìn)行集中處理,如果是BizException,根據(jù)錯(cuò)誤碼和錯(cuò)誤消息顯示給用戶對應(yīng)的頁面和錯(cuò)誤消息。如果是BizSystemException告知用戶系統(tǒng)錯(cuò)誤,并把錯(cuò)誤結(jié)果記入log。如果是其他異常和BizSystemException一致。這樣就減少了異常處理的復(fù)雜度,開發(fā)也不用關(guān)心什么檢查異常,運(yùn)行時(shí)異常。
如果是ajax請求在做web層時(shí),把返回的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)的錯(cuò)誤頁面了,并能把系統(tǒng)知道的和未知的異常記入log。
針對struts2還有個(gè)問題,在開發(fā)模式時(shí),struts2和webwork的異常打印在頁面,我們可以根據(jù)頁面輸出進(jìn)行調(diào)試。一但部署在生產(chǎn)環(huán)境,需要將這個(gè)模式關(guān)閉,log就沒有了。
<constant name="struts.devMode" value="false" /> |
為了記錄一些未知的錯(cuò)誤,需要做以下步驟:
1、將全局的異常映射頁面從struts2的包定義里去掉。如果不去掉,在webwork不會(huì)拋出異常也就找不到出了什么問題。
2、擴(kuò)展struts的DispatcherFilter捕捉未知的異常并記錄入log。
<global-exception-mappings> <exception-mapping exception="com.linktong.sdk.biz.exception.BizException" result="checkedException" /> </global-exception-mappings> |
這樣,基本的異常框架就搭建完成。更進(jìn)一步需要做的是:
1、分布式全局錯(cuò)誤碼體系,保證所有機(jī)器都共用一套錯(cuò)誤碼。
2、分布式部署,異常傳遞。可以采用hessian序列化錯(cuò)誤碼的機(jī)制,不用傳輸整個(gè)異常鏈節(jié)省帶寬。
3、集中l(wèi)ogger服務(wù)處理,所有機(jī)器的log統(tǒng)一發(fā)送到集中服務(wù)器處理。logger框架和log4j也有服務(wù)器的機(jī)制。
針對web框架、分布式部署、log服務(wù)器再討論:)