在一個framework的設計中,異常框架的設計佔據着很重要的位置,因爲它會直接影響到整個應用的健壯性、穩定性和易用性,因此筆者結合自己在產品開發中的經驗給出了一個異常框架的設計及實現與大家共享,有考慮不周或欠妥的地方還望大家一起討論,共同提高。
異常框架的總體結構圖
11.1異常框架總體結構圖
如上圖所示,
java
平臺框架的異常機制包括程序異常及業務異常。對於程序異常和業務異常我們設計一個
BaseException
基類,
BaseException
是一個
unchecked exception
(即繼承於
RuntimeException
),
javaeye
上有一篇關於
checked exception
和
unchecked exception
的討論長達幾十頁,偶在此就不多做解釋了,基於此平臺框架進行開發的應用可以對
BaseException
進行派生,例如
BizFocus-workflow
封裝了
PersistenceException
和
ServiceException
來對應於持久層異常和業務層異常。持久層和業務層的異常在捕獲之後通過
log4j
輸出到了程序日誌,然後繼續向外拋給展現層的
Action
控制類,
Action
不需要對異常進行捕獲,因爲
BizFocus-workflow
提供了一個基於
webwork
的異常攔截器(
ExceptionInterceptor
)對所有的
Action
的異常進行攔截。攔截後不同的異常在異常的國際化文件中取得異常提示信息展示給最終用戶。
1.2.1
異常信息的國際化封裝
如圖
1.1
所示,異常信息的國際化封裝主要由
Messages
接口及其實現類
MessageImpl
和
Message_CN.properties
、
Message.properties
兩個國際化文件組成。
Messages
接口如下:
public interface Messages {
public static final int ERROR_UNKNOWN = 0;
public static final int ERROR_SYSTEM = 1;
public static final int ERROR_WORKFLOW = 2;
public static final int NO_DECISIONVALUE_SET = 3;
………//
其它的異常代碼
}
MessageImpl
實現類的代碼如下:
public class MessageImpl implements Messages {
public MessageImpl() {
}
public static String getFormattedMessage(int i, Object aobj[]) {
String s;
try {
s = bundle.getString(String.valueOf(i));
if (aobj != null)
s = MessageFormat.format(s, aobj);
}
catch (MissingResourceException missingresourceexception) {
s = missingresourceexception.getMessage();
}
return s;
}
private static ResourceBundle bundle = ResourceBundle.getBundle("com.xxx.common.MessagesCN");
Message.properties
國際化文件如下:
0 = Unknown error
1 = System error
2 = Workflow error
3 = No decision vlaue was set
……
其它異常信息
Message_CN.properties
文件內容如下:
0 =
未知的錯誤
1 =
系統錯誤
2 =
工作流錯誤
3 =
您沒有設置工作流分支節點的決策值
……
其它異常信息
對於業務層和持久層異常的處理可以按照如下方式進行:
try {
//
業務邏輯代碼
……
} catch (XXXException ex) {
if (log.isDebugEnabled())//
如果是
debug
狀態,直接輸出異常的堆棧調用順序
ex.printStackTrace();
else//
不是
debug
狀態,則只輸出異常信息到日誌文件
log.error(BaseException.getLocalizedMessage(Messages.ERROR_WORKFLOWENGINE, null), ex);
throw new ServiceException(ex);//
最後將異常拋給外層
}
持久層和業務層的異常首先拋給了展現層的
Action
,在
Action
中對
Exception
不做任何處理直接拋出,例如
Action
的代碼如下:
public String execute() throws Exception {//
將
Exception
直接拋出
//
控制邏輯的代碼
……
}
1.2.4
通過
webwork
攔截器實現異常信息在界面的展示
在上一節中,
Action
將
Exception
直接拋出,因此在
webwork
攔截器中,對
Exception
進行攔截,將攔截的
Exception
進行分類,根據分類取得相關的異常提示信息,然後傳給統一的異常顯示頁面,用戶在異常頁面上就會看到不同的異常信息。
ExceptionInterceptor
代碼示例如下:
public class ExceptionInterceptor implements Interceptor {
private static final Log log = LogFactory.getLog(ExceptionInterceptor.class);
public static final String EXCEPTION = "exception";
public void destroy() {
//To change body of implemented methods use File | Settings | File Templates.
}
public void init() {
//To change body of implemented methods use File | Settings | File Templates.
}
public String intercept(ActionInvocation invocation) throws Exception {
String tipMessge = null;//
中文提示信息
String detailMessage = null;//
詳細的錯誤信息
try {
return invocation.invoke();
} catch (Exception e) {
if (e instanceof PersistenceException){//
判斷程序異常的類型
//
從國際化文件中取得相應的錯誤提示信息
tipMessge = ErrorMessage.getLocalizedMessage(Messages.ERROR_PERSISTENCELAYER);
}else if(e instanceof ServiceException){//
判斷程序異常的類型
tipMessge = ErrorMessage.getLocalizedMessage(Messages.ERROR_SERVICELAYER);
}
//
詳細的錯誤信息棧
detailMessage = invocation.getAction();
return EXCEPTION;//webwork
將導向
EXCEPTION
對應的錯誤信息頁面,
common/exception.ftl
}
}
}
上述代碼是一個比較詳細的異常攔截器的代碼,業務端可以基於此攔截器進行繼承,然後就可以很詳細地定製自己在開發過程中的異常。
common/exception.ftl
頁面的代碼如下:
<table align="left" class="common_input_table" style="width:100%;height:100%">
<tr>
<td align="center" class="common_td_lable">${action.getText('bizfocus.ui.errorPageTitle')}</td>
</tr>
<tr>
<td align="center" class="common_td_text" >
<div align="center">
<br><br><br><br>
<@ww.if test="errorMessage==null">
` <font color="red">${action.getText('bizfocus.ui.errormessage')}</font>
</@ww.if>
<@ww.else>
<a href=”detailMessage.action?detailMessage=<@ww.property value=’detailMessage’/>”> <font color="red"><@ww.property value="tipMessage"/></font>
</@ww.else>
</div>
</td>
</tr>
</table>
業務異常的處理,是在開發過程中經常遇到的問題,例如工作流引擎提供的根據業務數據自動決策分支節點的路由功能中,如果業務數據沒有傳遞給工作流引擎,則應該拋出一個沒有設置決策數據的業務異常
(NoDecisionValueSetException)
,因此在開發業務的過程中,開發者可以根據實際的業務情況進行異常類的定製開發,最後這些業務異常同樣被開發者自己擴展的異常攔截器進行攔截,最後根據業務異常的種類將不同的業務異常提示信息展示給最終用戶(例如:
“
您沒有設置決策數據給工作流引擎
”
)。