天天看點

SSH異常和日志處理方案

1         異常和日志的作用

1.1.    異常的作用

Java異常機制是為了對程式中可能出現的已知錯誤進行捕獲,并進行相應處理。從是否回報給使用者來看,存在兩類異常:

系統異常:這類異常由系統本身的低級異常引起,例如資料庫連接配接失敗、記憶體溢出、空指針異常等等,這類異常不需要出現在前台,因為使用者看不懂也沒有必要看到這些異常資訊。這類異常需要在日志中進行完整記錄以供日後開發人員進行檢視分析。

應用異常:即自定義異常,這類異常需要通過前台回報給使用者,友好提示使用者目前操作異常。應用異常通過系統異常轉換而來,例如建立使用者時,發生“主鍵沖突異常”,則需要在UserinfoDao中将“主鍵沖突異常”捕獲,并轉換為應用異常,異常提示資訊設為“該使用者名XXX已存在,請使用其它使用者名”,并将該異常資訊回報給前台。隻要系統異常影響到的使用者的目前操作,就必須給使用者提示資訊,比如“系統背景發生錯誤,請稍後再試”等。應用異常應按照提示方式的異常進行分類,對應不同的提示頁面。

1.2.    日志的作用

系統運作日志:記錄系統的運作情況,跟蹤代碼運作時軌迹;

異常和錯誤日志:記錄異常堆棧資訊,以供開發人員檢視分析;

業務日志:記錄業務資訊和使用者操作,例如使用者登入、删除資料、更新資料等。

2.      異常的處理原則

1、避免過大的try塊,不要把不會出現異常的代碼放到try塊裡面,盡量保持一個try塊對應一個或多個異常。

2、細化異常的類型,不要不管什麼類型的異常都寫成Excetpion。catch語句表示我們預期會出現某種異常,而且希望能夠處理該異常。異常類的作用就是告訴Java編譯器我們想要處理的是哪一種異常,然後針對具體的異常類進行不同的處理。例如在DAO層中我們應該隻捕獲SQLException或DataAccessException(Spring自定義的資料通路異常類)這些資料庫異常類,其他的異常NullPointException、NumberFormatException等應通過檢測代碼增加其健壯性來解決,而不應該通過try..catch(Exception e)…語句捕獲所有的異常。

3、不要把自己能處理的異常抛給别人,不要忽略捕獲的異常,捕獲到後要麼處理,要麼轉譯,要麼重新抛出新類型的異常。。處理方式包括:

Ø  處理異常。針對該異常采取一些行動,例如修正問題、提醒某個人或進行其他一些處理,要根據具體的情形确定應該采取的動作。再次說明,調用printStackTrace算不上已經“處理好了異常”。

Ø  重新抛出異常。處理異常的代碼在分析異常之後,認為自己不能處理它,重新抛出異常也不失為一種選擇。

Ø  把該異常轉換成另一種異常。大多數情況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被使用者了解的異常)。

4、如果對catch塊盡量保持一個塊捕獲一類異常,在catch語句中盡可能指定具體的異常類型,必要時使用多個catch。

例:

      try {

      } catch (Exception e) {

           e.printStackTrace();

           log.error("UserinfoDao execute() failed");

}

這段代碼捕獲了異常,但實際上對異常并沒有進行處理,可以算得上Java程式設計中的殺手。按照這個方式,在DAO層發生異常被忽略,Service層就認為DAO層運作正确,就不會復原,事務控制就沒有任何作用!!!

Ø  printStackTrace對調試程式有幫助,但程式調試階段結束之後,printStackTrace就不應再在異常處理子產品中擔負主要責任。

Ø  日志盡量在系統中的各個出口,例如Action、排程方法等處統一記錄,可減少工作量,況且日志"UserinfoDao execute() failed”并沒有說明異常的詳細資訊,沒有必要向日志輸出這句話。

Ø  即使catch中加入throw new RuntimeException(e);語句。如果捕獲的異常是RuntimeException,更沒有必要将異常再次轉化為RuntimeException,這樣不僅異常的本身的類型丢失,重新定義異常也造成一定消耗。

是以該處的處理原則應是:如果該處異常需要能轉化為業務異常回報給使用者,則需要捕捉低級異常并轉換成業務異常上抛,否則不做任何處理!!!

6、不要用try...catch參與控制程式流程,異常控制的根本目的是處理程式的非正常情況。

3.      開發中異常的處理方式

本系統使用SSH架構,DAO+Service+Action三層架構,捕獲原則是隻有将低級系統異常轉化為應用異常的需要才進行捕捉。

各層的處理方式如下:

DAO層:引發DAO異常的問題往往是不可恢複的,如資料連接配接失敗,SQL語句存在文法錯誤,強制捕捉的檢查型異常除了限制開發人員的自由度以外,并沒有提供什麼有意義的作用。是以,Spring的異常體系都是建立在運作期異常的基礎上,這些異常都繼承于DataAccessException(RuntimeException異常子類),是以,除了出于将低級系統異常轉化為應用異常的需要,沒有必要捕獲異常,讓DAO類自動上抛異常即可。

Service層:隻捕獲自定義應用異常,其他異常上抛。

Action:隻捕獲自定義應用異常,其他異常上抛。Struts2提供了異常攔截器,攔截器會将定義的異常捕獲,記錄日志,然後根據配置的異常的類型順序跳轉到相應的頁面。配置如下:

struts.xml配置檔案

<interceptor-ref name="defaultStack">

       <param name="exception.logEnabled">true</param><!--開啟日志記錄 -->

       <param name="exception.logLevel">error</param><!-- 日志級别為ERROR -->

</interceptor-ref>

<global-exception-mappings><!—異常類和跳轉頁面配置 -->

       <exception-mapping result="basicerror" exception="com.***.***.exception.BasicException"/>

       <exception-mapping result="error" exception="java.lang.Exception"/>

</global-exception-mappings>

com.***.***.exception.BasicException為自定義應用異常,如果用戶端的請求執行過過程中産生com.***.***.exception.BasicException異常,則會自動轉到basicerror頁面,進而給使用者相應的提示。

basicerror頁面

<%@ page language="java" contentType="text/xml; charset=UTF-8"       pageEncoding="UTF-8"%>${exception.message }

4.      自定義應用異常

異常名稱 說明
com.***.***.exception.BasicException 基礎異常類,本系統中所有的自定義異常類均需繼承本類
com.***.***.exception. DuplicateKeyException 主鍵沖突異常,繼承BasicException

Userinfo save方法異常處理

@Override

       public void save(Userinfo entity) {

              try {

                     super.save(entity);

              } catch (DataIntegrityViolationException e) {

                     if(e.getCause().getCause() instanceof SQLException){

                            SQLException sqlE = (SQLException)e.getCause().getCause();

                            if(sqlE.getErrorCode()==1){//ORACLE主鍵沖突異常代碼

                                   throw new DuplicateKeyException("使用者名:"+entity.getUserid()+"已存在,請使用其他使用者名");

                            }

                     }

              }

       }

5.      日志配置

繼續閱讀