2011-07-06 30 views
15

我使用舊服務層的項目工作,如果請求的記錄不存在或者由於調用者未被授權而無法訪問,該服務層會在許多地方返回空值。我正在談論ID所要求的特定記錄。舉例來說,像這樣:服務層檢查與未檢查異常

UserService.get(userId); 

我最近被推到這個API改變,或輔以拋出異常,而不是一個新的API。隨後關於檢查與未檢查的例外的爭論隨之而來。

從JPA/Hibernate et all的設計人員處獲悉,我建議未經檢查的異常可能是最合適的。我的觀點是,API的用戶無法合理預期從這些異常中恢復,在99%的情況下,我們最多可以通知應用程序用戶發生了一些錯誤。

將運行時異常傳播到通用處理機制將明顯降低處理邊緣情況異常所涉及的大量複雜性和所需的分支處理。但是,圍繞這種方法存在很多擔憂(正確)。

爲什麼JPA/EJB和Hibernate等項目的設計者選擇使用未經檢查的異常模型?有沒有非常好的理由呢?有什麼優點/缺點。是否應該使用這些框架的開發人員仍然在處理運行時異常時使用類似適配器包裝的方式來處理它們。

我希望這些問題的答案可以幫助我們對自己的服務層做出「正確」的決定。

回答

23

雖然我的觀點一致認爲,未經檢查的異常使一個更方便的API,這並不是我心目中最重要的好處。相反,它是這樣的:

拋出未經檢查的異常可幫助您避免嚴重的錯誤。

這是爲什麼。考慮到十位開發人員被迫處理被檢查的異常,你將得到二十種不同的策略來處理它們,其中許多是完全不合適的。這裏有一些更常見的壞方法:

  • 燕子。捕捉異常並完全忽略它。即使應用程序現在處於不穩定狀態,仍然繼續前進,好像什麼也沒有發生。
  • 日誌和吞下。抓住異常並記錄下來,認爲現在我們正在負責。然後繼續前進,好像什麼都沒有發生。
  • 神祕默認。捕捉異常,並將某些字段設置爲某個默認值,通常不會告訴用戶它。例如,如果您無法加載某些用戶的角色,只需選擇一個低權限角色並分配它即可。用戶想知道發生了什麼。
  • 愚蠢/危險的神祕默認。趕上例外,並設置一些真的很糟糕默認值字段。我在現實生活中看到的一個例子是:無法加載用戶的角色,因此請繼續並假設最好(即給予他們高特權角色以免給任何人造成不便)。
  • 誤報。開發人員不知道異常是什麼意思,所以只是想出了自己的想法。即使建立連接與該問題無關,IOException也會變爲「無法連接到服務器」。
  • 通過廣泛的捕獲面具和誤報。嘗試清除代碼,通過捕獲Exception或(gh)Throwable而不是該方法實際拋出的兩個檢查的異常。異常處理代碼不會嘗試區分(比如說)資源可用性問題(例如某些IOException)與完全代碼錯誤(例如NullPointerException)。事實上,它經常會任意挑選其中一個例外情況,並將每種例​​外情況誤報爲該類型的例外情況。
  • 面膜通過巨大的嘗試,並誤報。以前策略的一個變體是將一大堆異常聲明調用放在一個大的try塊的範圍內,然後捕獲ExceptionThrowable,因爲除此之外沒有其他操作可以處理所有拋出的異常。
  • 抽象不恰當的重投。即使異常不適合抽象(例如,從應該隱藏資源的服務接口重新拋出與資源相關的異常),也會重新處理異常。
  • 沒有包裝的情況下返回。重新發現異常(未經檢查或其他抽象 - 適當選中),但只需刪除嵌套異常,即可讓任何人有機會真正弄清楚發生了什麼。
  • 戲劇性的迴應。通過退出JVM來響應非致命異常。 (感謝this blog post這個。)

根據我的經驗,看到上面的方法比看到正確的響應更爲常見。許多開發人員 - 甚至是「高級」開發人員 - 都有這樣的想法:即使意味着應用程序處於不穩定狀態,也必須不惜一切代價來抑制異常。這是非常危險的錯誤。

未經檢查的異常有助於避免此問題。不知道如何處理異常情況的開發人員往往會將異常看作是克服不便的不便之處,並且他們不會阻礙他們發現。所以這些例外只是冒泡到頂端,在那裏他們提供了一個堆棧跟蹤,並且可以以一致的方式處理它們。在罕見的情況下,實際上有些事情比讓異常泡沫更好,但沒有什麼能阻止你。

2

我可能是錯的,但它可能與EJB容器處理異常的方式有關。從Best practices in EJB exception handling

要使用EJB容器的內部 看家,你必須有拋出 unchecked異常 你檢查的異常。

0

一般來說是拋出一個RuntimeException當你的代碼在較高水平不能處理它並不能做到對任何例外一個很好的做法。

就你的情況而言,如果結果成功,將返回一些結果,如果沒有找到結果,應該返回 ,如果在執行操作時出現任何錯誤,則拋出異常。

如果您只是向用戶顯示錯誤消息,並且無法對此問題進行更多處理,則可以取消選中該異常。

另一方面,如果您計劃在出現​​問題時採取一些備用步驟,那麼您應該拋出一個檢查異常。

例如, 我有一個代碼,這就好比是 -

扣除支付,然後 發送貨物信息。 如果(發送貨件的詳細信息不成功) 然後 退還款項 否則 發送成功郵件。

在上面的邏輯中,我發送貨運細節的邏輯應該是 拋出一個檢查過的異常,如果有任何問題,那麼我必須將金額退還給客戶。如果在這種情況下,我拋出一個未經檢查的異常,唯一 出現這種情況,用戶可能會收到錯誤消息。(除非你趕上的RuntimeException - 這是壞的)

感謝, Sathwick

0

未檢查異常有助於避免代碼混亂。例如考慮這個代碼

try 
    { 
     File file = new File("file"); 
     FileReader fileReader = new FileReader(file); 
     BufferedReader bufferedReader = new BufferedReader(fileReader); 
     String temp = ""; 
     StringBuilder result = new StringBuilder(""); 
     while ((temp = bufferedReader.readLine()) != null) 
     { 
      result.append(temp); 
     } 
     System.out.println(result); 
    } 
    catch (FileNotFoundException e) 
    { 
     e.printStackTrace(); 
    } 
    catch (IOException e) 
    { 
     e.printStackTrace(); 
    } 

我們處理兩種異常類型,但如果沒有,可以通過處理這些異常發生可操作的事件,爲什麼抓他們擺在首位?答案不是拋出檢查異常,因爲最終調用層次結構中的某些類將不得不處理它(或者將其拋出到JVM)。如果開發人員真的有興趣處理這些問題,那麼請捕獲解決問題的特定RuntimeException類。

持有多個異常Catch塊,在某種程度上減少這種混亂

1

我看到檢查/未經檢查的異常在以下幾個方面,(以從標準JDK線索)

  1. 使用經過檢查的異常,如果你的代碼/庫依賴於某些資源是外部的JVM(如.file/socket/remote api等,即JVM無法控制它)。你的代碼必須處理所有可能的錯誤情況。這是爲了確保你的程序是健壯的,並在這種資源出現問題的情況下正常失敗。

  2. 未經檢查的異常是嚴重的錯誤情況或者代碼中的真正BUGS,通常不應該在它發生後處理。您可以更正您的代碼,以便首先不會顯示此錯誤。 (方程式NLP,類別轉換,除以零等)