Java異常處理設計(一)

Zollty發表於2013-11-19

很多次的經驗教訓,讓我不得不重視異常處理。經常遇到的問題如下:
1)日誌不準確,錯誤原因難以查明!!
2)日誌量太大,查詢麻煩!!
3)哪裡需要記錄日誌,哪裡不用記錄日誌?往往隨心所欲!!
分析以上問題,深入研究,其實是缺少一個處理異常的原則和一個智慧的機制去“加工日誌”。

舉個真例項子:

[10/28/13 23:42:29:009 CST] 0000004d SystemErr     R org.springframework.transaction.TransactionSystemException: Could not roll back JDBC transaction; nested exception is com.ibm.websphere.ce.cm.ObjectClosedException: DSRA9110E: Connection is closed.
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager.java:279)
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:823)
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:800)
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:339)
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
[10/28/13 23:42:29:010 CST] 0000004d SystemErr     R    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at $Proxy53.deleteSsoTicketByStId(Unknown Source)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.proj.sso.ca.ServiceTicketServiceImpl.destroyServiceTicket(ServiceTicketServiceImpl.java:72)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.proj.sso.ca.action.SsoAction.execute(SsoAction.java:253)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at sun.reflect.GeneratedMethodAccessor178.invoke(Unknown Source)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at java.lang.reflect.Method.invoke(Method.java:600)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:450)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:289)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:252)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:011 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:239)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:239)
[10/28/13 23:42:29:012 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:252)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145)
[10/28/13 23:42:29:013 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:161)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
[10/28/13 23:42:29:014 CST] 0000004d SystemErr     R    at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:563)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:77)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.proj.webc.framework.XSSDefendFilter.doFilter(XSSDefendFilter.java:69)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:188)
[10/28/13 23:42:29:015 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:116)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterChain._doFilter(WebAppFilterChain.java:77)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:908)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:997)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.extension.DefaultExtensionProcessor.invokeFilters(DefaultExtensionProcessor.java:985)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.extension.DefaultExtensionProcessor.handleRequest(DefaultExtensionProcessor.java:905)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3826)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:276)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:931)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1583)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:186)
[10/28/13 23:42:29:016 CST] 0000004d SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:455)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:384)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:83)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.ws.tcp.channel.impl.AioReadCompletionListener.futureCompleted(AioReadCompletionListener.java:165)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.AbstractAsyncFuture.invokeCallback(AbstractAsyncFuture.java:217)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.AsyncChannelFuture.fireCompletionActions(AsyncChannelFuture.java:161)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.AsyncFuture.completed(AsyncFuture.java:138)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.ResultHandler.complete(ResultHandler.java:204)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.ResultHandler.runEventProcessingLoop(ResultHandler.java:775)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.io.async.ResultHandler$2.run(ResultHandler.java:905)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R    at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1550)
[10/28/13 23:42:29:017 CST] 0000004d SystemErr     R Caused by: com.ibm.websphere.ce.cm.ObjectClosedException: DSRA9110E: Connection is closed.
[10/28/13 23:42:29:018 CST] 0000004d SystemErr     R    at com.ibm.ws.rsadapter.jdbc.WSJdbcWrapper.createClosedException(WSJdbcWrapper.java:110)
[10/28/13 23:42:29:018 CST] 0000004d SystemErr     R    at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.rollback(WSJdbcConnection.java:3207)
[10/28/13 23:42:29:018 CST] 0000004d SystemErr     R    at org.springframework.jdbc.datasource.DataSourceTransactionManager.doRollback(DataSourceTransactionManager.java:276)
[10/28/13 23:42:29:018 CST] 0000004d SystemErr     R    ... 91 more

 

 

日誌不準確,比如:

try{

......此處省略200行

}catch{

throw new RuntimeException("操作失敗!");

}

這種日誌是不準確的,很糟糕。另一種:

try{

......此處省略200行

}catch{

throw new RuntimeException( e );

}

更糟糕。層層包裝之後e變得面目全非,很難識別真正的出錯原因。

我新設計的日誌包裝類,ExceptionWrapper,可以將錯誤資訊進行包裝,既便是多次包裝後,也不會失真。而且,對錯誤資訊和棧資訊可以進行定製。非常實用。

 

一、異常處理的基本步驟

 

以工具類為例,因為工具類是公用的,所以一般情況,不應該在工具類裡面寫日誌,而應該把錯誤訊號和錯誤資訊拋給外層,讓外層去決定:

1.  是否記錄錯誤日誌?

2.  如何後續處理?

 

因此,工具類的錯誤處理機制,要包含兩個資訊:

1. 異常資訊,2. 成功與否的標誌。

 

1)異常資訊:有兩種方法,(1)通過throw丟擲,(2)返回錯誤物件,比如定義一個ResultBean,把錯誤物件塞進去。

2)成功與否的標識:(1)通過是否丟擲異常來判斷;(2)可以通過返回狀態位表示,例如返回true代表成功,返回false代表失敗。

 

我們先討論throw錯誤這種情況。至於(2)那種情況,可應用我的OPGA設計模式,再此先不討論。

 

對於throw出來的錯誤資訊,應該是完整的、清晰的。

 

錯誤資訊有兩種形態,一種是自定義new出來的異常,通常業務邏輯異常,比如 age < 0 之類的。第二種,是內部程式出現錯誤時直接丟擲來的異常,比如 0/6 ,通常是未知其具體型別的。

 

對於這兩種錯誤形態,我們的處理方式也有著不同。比如對於我們自定義的異常,捕捉到之後,我們只需要getMessage,列印出message則一目瞭然。但是對於無法預料的錯誤型別,我們不但要列印出message,還要列印出異常的型別和棧資訊。

 

還比如,對於自定義的異常,

1)假設定義一個NoLogRuntimeException,在方法的內部記錄日誌,比如Action--Logic--Service--DAO四層結構,在Service層中記錄錯誤資訊,然後再丟擲一個NoLogRuntimeException。

2)Action層try-catch一切的錯誤,如果發現是NoLogRuntimeException,則不再記錄日誌(因為在Service層已經記錄過了),否則認為是“未知的錯誤”,列印具體的異常型別和異常堆疊。

 

=========================================================

二、下面是這個問題的詳細研究:

 

1、如何記錄日誌和除錯資訊

 

舉個例子:

 

如果在dao層log了日誌,又throw了錯誤,service層捕捉到錯誤後,又log日誌,這樣就造成了日誌重複。這是我們經常看到的情景。

 

如果在dao層不記錄日誌,只是throw錯誤,那麼“dao層”偷懶,造成的後果是,所以呼叫該dao的service,都要去log日誌,原本“一個人的工作”,現在每個人都要做,是否麻煩!!

 

注意到另外一個問題,捕捉錯誤應該儘量精確,最好是在哪兒出錯了,就在哪兒記錄,這樣能夠定位到錯誤所在的“行號”。這樣的錯誤資訊,才是最有價值的。可惜我們經常把錯誤層層轉換,層層上拋,結果“面目全非”,根本不知道底層到底發生了什麼錯誤。例如,一個大大的try-catch:

try{

       ......此處省略200行

}catch{

       throw new RuntimeException("操作失敗!");

}

 

這樣的寫法真的很糟糕,因為操作失敗的真正原因被掩蓋了,在外層捕捉到這個異常後,只知道是在這個方法內部出現了錯誤,錯誤的原因是“操作失敗!”。

 

 

稍微聰明一點的程式設計人員,會這麼做:

try{

       ......此處省略200行

}catch{

       throw new RuntimeException("操作失敗!原因:" + e.getMessage() );

}

這樣做是不夠好的,因為原始堆疊資訊丟失了,僅僅通過errorMsg是很難定位的。

再改進一下,寫成:

try{

       ......此處省略200行

}catch{

       throw new RuntimeException( e );

}

想想,這樣做又是很苦嘛!非要轉換一下,還不如直接丟擲異常:

try{

       ......此處省略200行

}catch{

       throw e;

}

這樣做還有意義嗎,還不如去掉try-catch,…………

 

 

意識到問題的普遍性,我們如何來設計這樣的程式?

 

 

有人推薦:“儘量清楚的捕捉異常,try-catch要定位精準”。

 

在我看來,有利有弊,try-catch範圍越小,意味著我們需要寫更多的try-catch,以前一個try-catch就搞定了,現在要寫很多的try-catch,而且寫起來很煩!try-catch範圍越小,遺漏的異常就越多,說不定哪個地方沒有捕捉到,就報空指標了……經常被坑,有沒有?

 

 

我研究這個錯誤處理機制大半年了,實施了很多種方案。目前探索出一條最佳的實踐路線。

 

先介紹一種初步改進過的方法:

 

 

1)對於自己定義的異常,比如age<0這種,做法如下:

       throw  new RuntimeException( xxxxxxxxxxxxxxxxxx );

把錯誤的原因描述清楚,更重要的是,要說明出錯時的環境!比如在操作某個訂單時,出錯,那麼這個訂單的ID是多少,也要一併列印出來。還比如出錯時的sql和引數是什麼,錯誤發生的時間等等,都一併記錄。即“記錄錯誤的原因和當時的環境狀態”。

 

 

2)對業務、邏輯異常,用自定義的異常,其他異常,一般情況下,不要轉換成自定義的異常。對於其他的異常,應該採用直接丟擲的方式。(轉換之後錯誤資訊不清晰)

 

一般情況下,出錯了,直接丟擲即可,滿足大多數的情景。還有少數情況下,出錯了,不希望程式退出,可以在內部捕捉。也就是在某些地方,允許出錯,正確時有正常的處理流程,出錯時有出錯時的處理流程。這樣,捕捉到的錯誤時,根據需要,記錄日誌資訊,日誌級別應該是debug或者warn級別的,儘量別定義成error級別,因為這種錯誤,是可以預料的,而且針對錯誤程式會自動進行處理,而error級別的錯誤,應該是一個最終的錯誤,導致程式沒辦法進行處理了,直接退出的那種錯誤。

 

 

同樣以Action--Logic--Service--DAO四層結構為例,

 

Action是最外層,我們在最外層做最終的日誌處理。

 

公用的dao層throw錯誤,把錯誤throw出去,不要自己處理。

 

同理,service層和logic層,一般都負責把錯誤資訊傳遞給最外層Action,而不自己處理( 對於某些可以處理的異常,比如業務邏輯異常,需要在內部處理 )。

 

action負責try-catch一切內部異常,然後記錄日誌。(在最外層最好要捕捉全面)

 

 

再進一步實踐,會發現,強制性地定義非執行時異常(比如Exception)是一種野蠻型的做法。為什麼非得讓別人去捕捉異常?異常出現的機率本來就小,實際上很多時候別人根本去不想去處理,大不了偶爾出了丟擲未捕獲的異常(由JVM或者Web容器自己去處理好了)。

 

因此,在這個層次上講,比較好的方法是:通常,我們只使用執行時異常——RuntimeException及其子類,它不強制性地要求別人去捕捉,出了錯,程式退出就OK了。

 

 

回過頭來看非執行時Exception異常,雖然具有強制性,一般不推薦使用,但是如果有特殊需要,比如要“提醒呼叫者,這個方法出錯後要進行一些特殊處理”,比如:

 

public int write(InputStream in) throws IOException;

 

我這個write方法是想提醒呼叫者,一定要及時捕捉它的異常資訊,如果出了錯,要進行一些特殊處理(即,關閉io流),以免造成一些垃圾殘留或者其他影響。

 

基於這樣的目的,我才推薦使用強制性的錯誤宣告,否則,最好用RuntimeException。

 

另外,以示和系統RuntimeException的區別,我建議,在專案中自定義一個RuntimeException,比如B2BRuntimeException。另外,對於一些大型的模組,還可以單獨定義一些有針對性的Exception型別。(當然,如果你有精力,可以多定義一些特定的錯誤型別,比如SourceNotFindException……)

 

 

除此之外,我透露一下我的一個工具(本文不做深入介紹),定義一個ExceptionWrapper,用於包裝異常,比如以前的做法:

try{

       ......

}catch{

       throw new RuntimeException( e );

}

如果要將Exception轉換成RuntimeExcption,現在的做法是:

try{

       ......

}catch{

       throw new ExceptionWrapper ( e );

}

 

這兩種做法效果是大不相同的,第一種做法,用RuntimeException去包裝,效果不理想,舉個例子:

       原始的錯誤棧資訊如下:

java.lang.ArithmeticException: / by zero

       at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:25)

       at org.zollty.framework.mvc.handler.support.AA.main(AA.java:60)

 

 

經過RuntimeException包裝後,錯誤棧資訊變成了:

java.lang.RuntimeException: java.lang.ArithmeticException: / by zero

       at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:28)

       at org.zollty.framework.mvc.handler.support.AA.main(AA.java:60)

Caused by: java.lang.ArithmeticException: / by zero

       at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:25)

       ... 1 more

 

這還只是一次包裝,如果經過層層包裝,棧資訊量就更大!

對於這種一大串的報錯資訊,很難抓住重點,而且是根本沒有必要的。

 

通過原始資訊,我們很容易定位:

 

是at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:25)出錯了。

 

但是經過RuntimeException包裝後,變成了:

at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:28)

 

這是錯誤經過包裝後throw出來的位置,而不是錯誤最原始的位置,要從Caused by:去尋找,才找得到源頭。

 

既然,存在這個問題,我們能不能把它變得更簡單一些呢。當然可以了。就是我設計的那個ExceptionWrapper(錯誤包裝器),這個東西可以存放原始的錯誤物件(即把e存起來),重新了getMessage等方法,當你呼叫它的方法時,實際上獲取的是最原始的錯誤資訊。也就是說,經過了ExceptionWrapper的包裝後,錯誤資訊不會失真。列印出來,還是

java.lang.ArithmeticException: / by zero

       at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:25)

       at org.zollty.framework.mvc.handler.support.AA.main(AA.java:60)

 

 

而且,你還可以這樣用:加入提示資訊(prompt)

try{

       ......

}catch{

       throw new ExceptionWrapper(e, "檔案刪除失敗");

}

然後列印錯誤時,會附加上你的提示資訊:

檔案刪除失敗 Caused by:

java.lang.ArithmeticException: / by zero

       at org.zollty.framework.mvc.handler.support.AA.ccc(AA.java:25)

       at org.zollty.framework.mvc.handler.support.AA.main(AA.java:60)

 

這樣的錯誤資訊,準確,簡潔。

 

至於有人想看這個ExceptionWrapper的原始碼(幾乎就是重新定義了java.lang.Throwable的大多數方法),等下一篇文章我再介紹吧。記住我的git地址,第一時間獲取更新資訊:

       https://github.com/zollty

 

 

 

 

 

 

相關文章