原創地址:http://www.cnblogs.com/Alandre/(泥沙磚瓦漿木匠),須要轉載的,保留下! 文章宗旨:Talk is cheap show me the code.html
摘要:java
異常處理概述web
學習內容:sql
思考:函數
java web 中的異常處理源碼分析
異常處理,英文名爲exceptional handling, 是代替日漸衰落的error code方法的新法,提供error code 所未能具體的優點。異常處理分離了接收和處理錯誤代碼。這個功能理清了編程者的思緒,也幫助代碼加強了可讀性,方便了維護者的閱讀和理解。
java語言中,異常處理能夠確保程序的健壯性,提升系統的可用率.可是java api 提供的異常都是比較低級的,因此有了'提倡異常封裝’
異常封裝有三個優勢: 1)提升系統的友好性 2)提升性能的可維護性 3)解決java異常機制自身的缺陷
提升系統的友好性
系統的友好性,就像系統和開發人員等握手與交流.好的系統對象,會展示出交流時候所須要的一切.所以,良好的友好性須要良好的代碼告訴開發人員和用戶.開發人員要找須要打印出堆棧信息.
show the code:
public void doStuff() throws MyBussinessException { try { InputStream iStream = new FileInputStream("無效文件.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); throw new MyBussinessException(e);//此處自定義一個MyBussinessException } }
::throw new MyBussinessException(e);
在這裏,無效文件致使了兩點:文件未找到和該業務出現問題.所以,在文件未找到以後咱們能夠繼續根據須要告之其餘異常.
提升性能的可維護性
如何提搞可維護性,你們不能一味的進行這樣操做,就拋出Exception,這樣會致使別人看你的代碼徹底check不到異常點.下面的代碼是不可取的:
public void doStuff() { try { //do something } catch (Exception e) { e.printStackTrace(); } }
正確的作法是對異常進行分類處理,並進行封裝輸出.
show the code:
public void doStuff() { try { //do something } catch (FileNotFoundException e) { log.info("文件未找到!文件爲:xxx"); } catch (SecurityException e) { log.error("無權訪問,緣由:xxx"); e.printStackTrace(); } }
catch{}
catch{}
這樣子,直接在代碼層級上分析便可,代碼異常在哪裏拋出.維護人員看到這樣的異常也會有了準確的判斷.
解決java異常機制自身的缺陷
先拋出個問題:例如註冊時,要對不少進行檢驗.像密碼,用戶名,郵箱…...這樣狀況下,咱們必需要在一個註冊方法裏面拋出不少個異常.所以,正確的方法是封裝異常,創建異常容器,一次性對某對象進行校驗,而後返回全部異常.
show the code
異常容器:
import java.util.ArrayList;import java.util.List;public class MyException extends Exception { //容納全部異常 private List<Throwable> causes = new ArrayList<Throwable>(); //構造函數 public MyException(List<? extends Throwable> _causes) { causes.addAll(_causes); } //讀取全部的異常 public List<Throwable> getExceptions() { return causes; } }
處理異常:
public static void doStuff() throws MyException { List<Throwable> list = new ArrayList<Throwable>(); //第一個邏輯片斷 try { //Do Something } catch (Exception e) { list.add(e); } //第二個邏輯片斷 try { //Do Something two } catch (Exception e) { list.add(e); } //檢查是否有必要拋出異常 if(list.size() > 0) { throw new MyException(list); } }
咱們作的JEE項目時候,通常會有三層的結構:持久層,邏輯層,展示層.異常也是如此的,當咱們各個層之間傳遞異常,咱們就須要先封裝,而後傳遞.簡要的就是採用異常傳遞異常:
show the code:
[摘自源碼分析]
/** * * 執行 SQL 查詢。 * * @param isCallable : 是否使用 CallableStatment 執行查詢 * @param sql : 查詢語句 * @param params : 查詢參數 * @return 結果集 * */protected List<Object[]> query(boolean isCallable, String sql, Object ... params) { List<Object[]> result = new ArrayList<Object[]>(); ResultSet rs = null; PreparedStatement pst = null; try { Connection conn = getSession(); pst = JdbcUtil.prepareStatement(conn, sql, isCallable); JdbcUtil.setParameters(pst, params); rs = pst.executeQuery(); int cols = rs.getMetaData().getColumnCount(); while(rs.next()) { Object[] objs = new Object[cols]; for(int i = 0; i < cols; i++) objs[i] = rs.getObject(i + 1); result.add(objs); } } catch (SQLException e) { throw new JdbcException(e); } finally { JdbcUtil.closeSqlObject(pst, rs); } return result; }
從中咱們能夠抽取的看到:
catch (SQLException e) { throw new JdbcException(e); }
jdbc執行SQL語句查詢的時候,先拋出SQLException ,而後就像一條鏈同樣,下一步告訴別人是JDBC的異常.下面體會經典,休息下:
全部受檢異常(Checked Exception)是好事,爲什麼要儘量轉化爲非,也就是(Unchecked Exception)呢?個人理解是:受檢異常威脅到系統的安全性,穩定性,可靠性,正確性時,不能轉換爲非受檢異常.
也就是說,其中存在的受檢異常有缺點,轉換成Unchecked Exception就輕鬆解決了.
受檢異常使接口聲明脆弱
show the code:
interface User { //修改用戶名密碼,拋出安全異常 public void changePass() throws MySecurityException; }
throws MySecurityException;
這裏面不必定只是一個異常,然而定義了異常,會增長了接口的不穩定性,這就存在了面向對象設計的嚴重褻瀆,若是要改變的話,又破壞了封裝性.
另外,受檢異常還有兩個缺點:
受檢異常使代碼可讀性下降
受檢異常增長了開發工做量
「性能問題不是拒絕異常的藉口」 就當一個常見的登陸用例.咱們常常會添加一個例外的事件:」連續登陸3次登陸失敗,暫時鎖定用戶賬號.」這樣這個例外的事件就是一個異常處理.
show the code
public void login() { try { // 正常登陸 } catch (InvalidLoginNameException e) { // 用戶名無效 } catch (InvalidPasswordException e) { // 密碼錯誤 } catch (TooManyLoginsException e) { // 屢次登陸失敗 } }
這樣子一來,代碼邏輯很清晰.可是這樣子就拋出了一個主意.這樣子有代價:
性能比較慢
java的異常處理機制確實比較慢,這個性能慢是相對的.相對那些基礎類型:String Integer…等.有人測試結果以下:
測試結果: (運行環境:xen虛擬機,5.5G內存,8核;jdk1.6.0_18) (10個線程,建立10000000個對象所需時間) 普通Java對象 45284 MS 普通java異常 205482 MS 改進的Java業務異常 16731 MS
至關於建立每一個異常對象是普通對象的五倍.可是數量級上是 MS,在一個系統中,如此微小的性能消耗是能夠容許的.
經驗之談:」用對了地方就好,用錯了地方就很差。」這是個人師傅跟我說的,他教會了不少.太很抽象,我想我會慢慢學會的.
實際J2EE項目中,一般一個頁面請求到達後臺之後,首先是到MVC中的controller,在controller層會調用業務邏輯層service,而在service層會調用持久層dao進而得到數據,再將得到的數據一層一層返回到controller層,而後經過controller控制層轉發到指定頁面.
可能存在的異常:
dao層可能會發生SQLException異常
service層可能會發生NullPointException異常,
controller層可能會發生IOException異常,RuntimeException異常
正確的處理方式
根據上面知識的說法:咱們該用如下的方法來實現
show the code:
@RequestMapping(value = "/courseTeacherRelationAdd")public @ResponseBody String courseTeacherRelationAdd(String courseID,String teacherInfoID,CourseTeacherRelation courseTeacherRelation) { try { int sign = courseTeacherRelationService.saveOrUpdateCourseTeacherRelation(courseID,teacherInfoID,courseTeacherRelation); if( sign == Constant.RESULT_SUCCESS ) return successResponse("保存成功","../courseTeacherRelation/courseTeacherRelations" , "courseTeacherRelations"); } catch (Exception e) { throw new EntityException("Error! when save the entity",e); } return failResponse("保存失敗"); }
throw new EntityException("Error! when save the entity",e);
這裏用了鏈式異常拋出:EntityException是自定義的異常類:
public class EntityException extends RuntimeException { private static final long serialVersionUID = 1L; public EntityException() { super(); } public EntityException(String message) { super(message); } public EntityException(String message, Throwable cause) { super(message, cause); } }
天然還有些什麼攔截器拋出異常,在這裏就不詳細展開討論了.