1. 程式人生 > >javaWeb 專案整體異常處理方案

javaWeb 專案整體異常處理方案

http://www.iteye.com/topic/1073599

在實際的j2ee專案中,系統內部難免會出現一些異常,如果把異常放任不管直接列印到瀏覽器可能會讓使用者感覺莫名其妙,也有可能讓某些使用者找到破解系統的方法。

出來工作一年時間了,我也大概對異常處理有了一些瞭解,在這呢小弟簡單介紹下個人對異常處理的見解,拋磚引玉,希望各位大神提出寶貴的意見和建議。

就拿spring+struts2+hibernate專案說明:通常一個頁面請求到後臺以後,首先是到action(也就是所謂mvc的controller),在action層會呼叫業務邏輯service,servce層會呼叫持久層dao獲取資料。最後執行結果會彙總到action,然後通過action控制轉發到指定頁面,執行流程如下圖所示:



 

而這三層其實都有可能發生異常,比如dao層可能會有SQLException,service可能會有NullPointException,action可能會有IOException,一但發生異常並且程式設計師未做處理,那麼該層不會再往下執行,而是向呼叫自己的方法丟擲異常,如果dao、service、action層都未處理異常的話,異常資訊會拋到伺服器,然後伺服器會把異常直接列印到頁面,結果就會如下圖所示:



 

其實這種錯誤對於客戶來說毫無意義,因為他們通常是看不懂這是什麼意思的。

剛學java的時候,我們處理異常通常兩種方法:①直接throws,放任不管;②寫try...catch,在catch塊中不作任何操作,或者僅僅printStackTrace()把異常列印到控制檯。第一種方法最後就造就了上圖的結果;而第二種方法更杯具:頁面不報錯,但是也不執行使用者的請求,簡單的說,其實這就是bug(委婉點:通常是這樣)!

那麼發生異常到底應該怎麼辦呢?我想在大家對java異常有一定了解以後,會知道:異常應該在action控制轉發之前儘量處理,同時記錄log日誌,然後在頁面以友好的錯誤提示告訴使用者出錯了。大家看下面的程式碼:

Java程式碼  收藏程式碼
  1. //建立日誌物件  
  2. Log log = LogFactory.getLog(this.getClass());  
  3. //action層執行資料新增操作  
  4. public String save(){  
  5.    try{  
  6.          //呼叫service的save方法  
  7.          service.save(obj);  
  8.    }catch(Exception e){  
  9.          log.error(...);   //記錄log日誌  
  10.       return "error"; 到指定error頁面  
  11.    }  
  12.    return "success";  
  13. }  

如果按照上面的方式處理異常以後,我們使用者最後看到的頁面可能就會是下面這種形式(我想這種錯誤提示應該稍微友好點了吧):



 

然後我們回到剛才處理異常的地方,如果大家積累了一些專案經驗以後會發現使用上面那種處理異常的方式可能還不夠靈活:

①因為spring把大多數非執行時異常都轉換成執行時異常(RuntimeException)最後導致程式設計師根本不知道什麼地方應該進行try...catch操作

②每個方法都重複寫try...catch,而且catch塊內的程式碼都很相似,這明顯做了很多重複工作而且還很容易出錯,同時也加大了單元測試的用例數(專案經理通常喜歡根據程式碼行來估算UT case)

③發生異常有很多種情況:可能有資料庫增刪改查錯誤,可能是檔案讀寫錯誤,等等。使用者覺得每次發生異常都是“訪問過程中產生錯誤,請重試”的提示完全不能說明錯誤情況,他們希望讓異常資訊更詳盡些,比如:在執行資料刪除時發生錯誤,這樣他們可以更準確地給維護人員提供bug資訊。

如何解決上面的問題呢?我是這樣做的:JDK異常或自定義異常+異常攔截器

struts2攔截器的作用在網上有很多資料,在此不再贅述,我的異常攔截器原理如下圖所示:



 首先我的action類、service類和dao類如果有必要捕獲異常,我都會try...catch,catch塊內不記錄log,通常是丟擲一個新異常(自定義異常),並且註明錯誤資訊:

Java程式碼  收藏程式碼
  1. //action層執行資料新增操作  
  2. public String save(){  
  3.    try{  
  4.          //呼叫service的save方法  
  5.          service.save(obj);  
  6.    }catch(Exception e){  
  7.       //你問我為什麼丟擲Runtime異常?因為我懶得在方法後寫throws  xx  
  8.       throw new RuntimeException("新增資料時發生錯誤!",e);  
  9.   }  
  10.    return "success";  
  11. }  

然後在異常攔截器對異常進行處理,看下面的程式碼:

Java程式碼  收藏程式碼
  1. public String intercept(ActionInvocation actioninvocation) {  
  2.         String result = null// Action的返回值  
  3.         try {  
  4.             // 執行被攔截的Action,期間如果發生異常會被catch住  
  5.             result = actioninvocation.invoke();  
  6.             return result;  
  7.         } catch (Exception e) {  
  8.             /** 
  9.              * 處理異常 
  10.              */  
  11.             String errorMsg = "未知錯誤!";  
  12.             //通過instanceof判斷到底是什麼異常型別  
  13.             if (e instanceof BaseException) {  
  14.                 BaseException be = (BaseException) e;  
  15.                 be.printStackTrace(); //開發時列印異常資訊,方便除錯  
  16.                 if(be.getMessage()!=null||Constants.BLANK.equals(be.getMessage().trim())){  
  17.                     //獲得錯誤資訊  
  18.                     errorMsg = be.getMessage().trim();  
  19.                 }  
  20.             } else if(e instanceof RuntimeException){  
  21.                 //未知的執行時異常  
  22.                 RuntimeException re = (RuntimeException)e;  
  23.                 re.printStackTrace();  
  24.             } else{  
  25.                 //未知的嚴重異常  
  26.                 e.printStackTrace();  
  27.             }  
  28.             //把自定義錯誤資訊  
  29.             HttpServletRequest request = (HttpServletRequest) actioninvocation  
  30.                     .getInvocationContext().get(StrutsStatics.HTTP_REQUEST);  
  31.             /** 
  32.              * 傳送錯誤訊息到頁面 
  33.              */  
  34.             request.setAttribute("errorMsg", errorMsg);  
  35.             /** 
  36.              * log4j記錄日誌 
  37.              */  
  38.             Log log = LogFactory  
  39.                     .getLog(actioninvocation.getAction().getClass());  
  40.             if (e.getCause() != null){  
  41.                 log.error(errorMsg, e);  
  42.             }else{  
  43.                 log.error(errorMsg, e);  
  44.             }  
  45.             return "error";  
  46.         }// ...end of catch  
  47.     }  

 需要注意的是:在使用instanceof判斷異常型別的時候一定要從子到父依次找,比如BaseException繼承與RuntimeException,則必須首先判斷是否是BaseException再判斷是否是RuntimeException。

最後在error JSP頁面顯示具體的錯誤訊息即可:

Java程式碼  收藏程式碼
  1. <body>  
  2. <s:if test="%{#request.errorMsg==null}">  
  3.     <p>對不起,系統發生了未知的錯誤</p>  
  4. </s:if>  
  5. <s:else>  
  6.     <p>${requestScope.errorMsg}</p>  
  7. </s:else>  
  8. </body>  

以上方式可以攔截後臺程式碼所有的異常,但如果出現數據庫連線異常時不能被捕獲的,大家可以使用struts2的全域性異常處理機制來處理:

Java程式碼  收藏程式碼
  1. <global-results>  
  2.     <result name="error" >/Web/common/page/error.jsp</result>  
  3. </global-results>  
  4. <global-exception-mappings>  
  5.     <exception-mapping result="error" exception="java.lang.Exception"></exception-mapping>  
  6. </global-exception-mappings>  

上面這是一個很簡單的異常攔截器,大家可以使用自定義異常,那樣會更靈活一些。

以上異常攔截器可以使用其它很多技術替換:比如spring aop,servlet filter等,根據專案實際情況處理。

【補充】ajax也可以進行攔截,但是因為ajax屬於非同步操作,action通過response形式直接把資料返回給ajax回撥函式,如果發生異常,ajax是不會執行頁面跳轉的,所以必須把錯誤資訊返回給回撥函式,我針對json資料的ajax是這樣做的:

Java程式碼  收藏程式碼
  1. /** 
  2.  * 讀取檔案,獲取對應錯誤訊息 
  3.  */  
  4. HttpServletResponse response = (HttpServletResponse)actioninvocation.getInvocationContext().get(StrutsStatics.HTTP_RESPONSE);  
  5. response.setCharacterEncoding(Constants.ENCODING_UTF8);  
  6. /** 
  7.  * 傳送錯誤訊息到頁面 
  8.  */  
  9. PrintWriter out;  
  10. try {  
  11.     out = response.getWriter();  
  12.     Message msg = new Message(errorMsg);  
  13.     //把異常資訊轉換成json格式返回給前臺  
  14.     out.print(JSONObject.fromObject(msg).toString());  
  15. catch (IOException e1) {  
  16.     throw e;  
  17. }  
houfeng0923 寫道 不錯,我之前基於struts1也是類似這樣的處理思路; 
這樣在業務和資料層到異常可以只捕獲再丟擲新的定義異常;剩下交攔截器處理。 
記錄log的工作還是沒從業務和資料層剝離出來,傳統的log記錄是在資料層-業務層-控制層分別記錄來跟蹤出現bug的程式流程;如果在攔截器那裡記錄日誌,又擔心沒法記錄詳細的bug出現流程資訊;是否根據捕獲的exception例項中可以獲取到?如果可以,那如果存在業務層和資料層都有spring代理管理的話,又該如何?

異常的重要資訊是放在Throwable中的,只要Throwable不要丟失,就不會有問題 
try{ 
   .... 
}catch(IOException e){ 
   throw new RuntimeException(e); 


業務層和資料層由spring代理也不會有問題,只要spring未對異常進行處理消化,最終異常始終會到action層被攔截器捕獲。
damoqiongqiu 寫道 不錯,思路清晰,格式良好 
如果要針對不同的異常型別做出不同的響應又該如何呢?

這位朋友過獎了。 

不同異常型別依然通過instanceof來判斷,判斷後就可以做不同操作 
if (e instanceof Exception1) { 
  Exception1 e1 = (Exception1) e; 
  //做其它不不同響應 
} else if(e instanceof Exception2){ 
  Exception2re = (Exception2)e; 
  //做其它不不同響應 
}
白糖_ 寫道 damoqiongqiu 寫道 不錯,思路清晰,格式良好 
如果要針對不同的異常型別做出不同的響應又該如何呢?

這位朋友過獎了。 

不同異常型別依然通過instanceof來判斷,判斷後就可以做不同操作 
if (e instanceof Exception1) { 
  Exception1 e1 = (Exception1) e; 
  //做其它不不同響應 
} else if(e instanceof Exception2){ 
  Exception2re = (Exception2)e; 
  //做其它不不同響應 
}
個人認為,根據異常類的型別來區分異常是不是有些臃腫並且難以擴充套件。我比較喜歡的做法是,自己定義一個BaseException,繼承自RuntimeException,然後給BaseException里弄一個errorCode,用errorCode來區分異常型別,這樣你只需要維護一張errorCode與異常型別的對應表,就不用定義那麼多Exception類了,維護方便,擴充套件也方便。拋異常的時候只需設定errorCode,捕獲異常之後根據errorCode去判斷異常型別並做相應處理。

相關推薦

javaWeb 專案整體異常處理方案

http://www.iteye.com/topic/1073599 在實際的j2ee專案中,系統內部難免會出現一些異常,如果把異常放任不管直接列印到瀏覽器可能會讓使用者感覺莫名其妙,也有可能讓某些使用者找到破解系統的方法。 出來工作一年時間了,我也大概對異常處理有了

java web專案整體異常處理機制

在實際的j2ee專案中,系統內部難免會出現一些異常,如果把異常放任不管直接列印到瀏覽器可能會讓使用者感覺莫名其妙,也有可能讓某些使用者找到破解系統的方法。 出來工作一年時間了,我也大概對異常處理有了一些瞭解,在這呢小弟簡單介紹下個人對異常處理的見解,拋磚引玉,希望各位大

SSM專案統一異常處理方案

java開發說明 專案中的異常處理是專案開發中的重要部分,合理的異常處理方案,對於提高專案開發效率,影響較大。 SSM專案中採用如下方案進行異常處理: 1 對於DAO層丟擲的sql異常,已經由myBatis包裝成了執行時異常,不需要再進行包裝 2 對於S

springmvc、 springboot 專案全域性異常處理

異常在專案中那是不可避免的,通常情況下,我們需要對全域性異常進行處理,下面介紹兩種比較常用的情況。 準備工作: 在捕獲到異常的時候,我們通常需要返回給前端錯誤碼,錯誤資訊等,所以我們需要手動封裝一個jsonData物件,這裡為了方便,我直接通過返回布林型別來進行判斷成功或失敗,在實際的專案中

11-10關於java專案異常處理

這是我們在java專案常見的異常:      1. java.lang.nullpointerexception   這個異常大家肯定都經常遇到,異常的解釋是"程式遇上了空指標",簡單地說就是呼叫了未經初始化的物件或者是不存在的物件,這個錯誤經常出現在建立

四:JVM調優與常見異常處理方案

在jvm調優之前,我們必須先了解jvm的記憶體模型與GC回收機制,這些在我前面的文章裡面有介紹!接下來我們通過一個案例來調整jvm效能。 一:調優案例:   1.1 編寫demo import java.text.DecimalFormat; /** -XX:+PrintGC &

java專案異常處理情況

一,基本概念   異常是程式在執行時出現的不正常情況。是Java按照面向物件的思想將問題進行物件封裝。這樣就方便於操作問題以及處理問題。  異常處理的目的是提高程式的健壯性。你可以在catch和finally程式碼塊中給程式一個修正機會,使得程式不因不可控制的異常而影響程式的流程。同時,通過獲取Java異常

[轉]JVM調優原理與常見異常處理方案

在jvm調優之前,我們必須先了解jvm的記憶體模型與GC回收機制,這些在我前面的文章裡面有介紹!接下來我們通過一個案例來調整jvm效能。 一 測試案例: 1.1 編寫demo import java.text.DecimalFormat; /** -XX:+PrintGC

【Web】Web開發中的異常處理方案

我認為最合理的做法: 1、dao層不捕獲異常、不丟擲異常:spring框架將底層的資料庫checked異常封裝成unchecked異常了 2、service層捕獲異常,並丟擲自定義unchecked異常,異常中不定義狀態碼:checked異常預設情況事務不會回滾 3、controller層捕獲異常,並丟擲自定

javaweb專案高併發處理

併發是什麼,之前我覺得就是對資料的一個安全性操作,這樣理解也沒有錯,因為這是資料的併發,那麼什麼是併發呢? 併發,在作業系統中,是指一個時間段中有幾個程式都處於已啟動執行到執行完畢之間,且這幾個程式都是在同一個處理機上執行,但任一個時刻點上只有一個程式在處理機上執行。這

介面呼叫常見異常處理方案

一個遠端伺服器會暴露各種各樣的對外服務,我們稱之為service1 2 3 某個客戶端會呼叫其中某個service,在不出異常的情況下服務可能會很穩定, 但是一旦出現了異常,如果沒有良好的異常處理機制及日誌會導致後續查詢問題 很困難,特別是大型分散式結構的系統 Server

struts2之json請求的異常處理方案

大家都知道,使用struts2的異常處理機制,只要幾行配置,就可以在發生異常時,跳轉到我們指定的頁面,並顯示出相應的異常資訊,具體的使用操作過程如下: 1)struts.xml <struts> <include file="struts-default

javaWeb專案(Spring、Struts2框架)gbk轉gb180整體方案(主要用於解決生僻字)

一、需要需改點:     1:jsp中指定瀏覽器編碼格式,修改後:<%@ page contentType="text/html; charset=GB18030"%>      2:過濾器:   

C#進階系列——WebApi 異常處理解決方案(轉)

機制 輸出 ges 如果 但是 rom lba slist 解決 出處:http://www.cnblogs.com/landeanfen/p/5363846.html 閱讀目錄 一、使用異常篩選器捕獲所有異常 二、HttpResponseException自

異常-JDK7針對多個異常處理方案

exceptio 問題 .cn itcast exce 可能 ima ice 方式 1 package cn.itcast_02; 2 3 /* 4 * JDK7出現了一個新的異常處理方案: 5 * try{ 6 * 7 *

java JVM常見的四大異常處理方案

maxperm 就是 locate stack 修飾 gpo 異常 native 思路 區域 作用 異常 控制參數 解決思路 java堆 存放對象的實例。 java.lang.OutOfMemory Error:Java heap space -Xms(初始化堆

JavaWEB專案報java.io.IOException: Broken pipe異常

分析系統日誌的時候,經常遇到java.io.IOException: Broken pipe異常,但是又找不到具體原因,很難復現,之前也在網上查過這個問題,但是相關的資料很少,今天重新搜尋了相關問題,找到了些靈感。 這個異常是tomcat的connector在執行寫操作的時候發生的,

專案開發(異常處理流程)

採用的是SpringMVC中的統一異常處理機制 編寫異常類 異常分為Exception分為檢查型異常 和非檢查型異常,這裡通過繼承Exception的類屬於非檢查型異常 package com.fdd.exception; public class MyException extend

JAVA專案中常用的異常處理情況總結

1. java.lang.nullpointerexception 這個異常大家肯定都經常遇到,異常的解釋是"程式遇上了空指標",簡單地說就是呼叫了未經初始化的物件或者是不存在的物件,這個錯誤經常出現在建立圖片,呼叫陣列這些操作中,比如圖片未經初始化,或者圖片建立時的路徑錯誤等等。對陣列操作中出現空指標

JAVA專案中的常用的異常處理

1. java.lang.nullpointerexception 這個異常大家肯定都經常遇到,異常的解釋是"程式遇上了空指標",簡單地說就是呼叫了未經初始化的物件或者是不存在的物件,這個錯誤經常出現在建立圖片,呼叫陣列這些操作中,比如圖片未經初始化,或者圖片建立時的路徑錯誤等等。對陣列操作中出現空指標