java平臺統一異常框架的設計與實現

  在一個framework的設計中,異常框架的設計佔據着很重要的位置,因爲它會直接影響到整個應用的健壯性、穩定性和易用性,因此筆者結合自己在產品開發中的經驗給出了一個異常框架的設計及實現與大家共享,有考慮不周或欠妥的地方還望大家一起討論,共同提高。

 
1.1  Java 異常框架 總體設計
異常框架的總體結構圖

 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 = 您沒有設置工作流分支節點的決策值
…… 其它異常信息
1.2.2 業務層和持久層異常的國際化處理
對於業務層和持久層異常的處理可以按照如下方式進行:
        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);// 最後將異常拋給外層
        }
 
1.2.3 展現層異常的國際化處理
持久層和業務層的異常首先拋給了展現層的 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>
 
1.3  業務異常的擴展
1.3.1Exception 的繼承機制
業務異常的處理,是在開發過程中經常遇到的問題,例如工作流引擎提供的根據業務數據自動決策分支節點的路由功能中,如果業務數據沒有傳遞給工作流引擎,則應該拋出一個沒有設置決策數據的業務異常 (NoDecisionValueSetException) ,因此在開發業務的過程中,開發者可以根據實際的業務情況進行異常類的定製開發,最後這些業務異常同樣被開發者自己擴展的異常攔截器進行攔截,最後根據業務異常的種類將不同的業務異常提示信息展示給最終用戶(例如: 您沒有設置決策數據給工作流引擎 )。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章