簡體   English   中英

java:包裝已檢查異常的標准方法

[英]java: standard method of wrapping checked exceptions

我有一個相當詳細的問題,關於包裝已檢查異常的正確方法,以及Guava的方式。 (道歉,但我想讓我的思考過程失敗)


標准的Runnable接口如下所示:

public interface Runnable
{
   public void run();
}

其中run()不能拋出已檢查的異常。

因此,如果我想要一個Runnable用於包裝拋出已檢查異常的任務,並且我打算讓調用Runnable.run()的東西處理這些異常,而不是在Runnable.run()本身,我必須在未經檢查的異常中包裝異常。

所以有一段時間我用的是:

Runnable r = new Runnable {
   @Override public void run()
   {
       try {
          doNastyStuff();
       }
       catch (NastyException e)
       {
          throw new RuntimeException(e);
       }
   }      
};

然后我可以在上層處理RuntimeException。 除了我想,我真正想要的是分別處理一個包裝的異常,因為我知道它的語義是包裝一個已檢查的異常,所以我寫了這個幫助類:

/**
 * Wrapped exception: the purpose of this is just to wrap another exception,
 * and indicate that it is a wrapped exception
 */
public class WrappedException extends RuntimeException
{
    /**
     * @param t any throwable
     */
    public WrappedException(Throwable t)
    {
        super(t);
    }
}

然后我可以這樣做:

/* place that produces the exception */
...
catch (NastyException e)
{
   throw new WrappedException(e);
}

...
/* upper level code that calls Runnable.run() */
try
{
   ...
   SomeOtherNastyCode();
   r.run();
   ...
}
catch (SomeOtherNastyException e)
{
   logError(e);
}
catch (WrappedException e)
{
   logError(e.getCause());
}

它看起來很棒。

但是現在我想,如果我想在庫中使用它以及使用該庫的應用程序,現在它們都依賴於WrappedException,所以它應該真正存在於我可以包含在任何地方的基礎庫中。

這讓我想到,也許Guava在某處有一個標准的WrappedException類,因為我現在默認將Guava包含為依賴。 所以我可以做到

throw new WrappedException(e);

要么

throw Exceptions.wrap(e);

要么

Exceptions.rethrow(e);

我只是在Guava中環顧四周,發現Throwables的 Throwables.propagate()看起來很相似,但它只是在RuntimeException包含已檢查的異常,而不是RuntimeException的特殊子類。

哪種方法更好? 與RuntimeException相比,我不應該使用特殊的WrappedException嗎? 我的頂級代碼想知道添加信息值的最重要的異常。

如果我有一個包裝NastyException的RuntimeException包裝NullPointerException,包裝RuntimeException不會添加信息值,我不關心它,所以我記錄的錯誤將是NastyException。

如果我有一個包含NastyException的IllegalArgumentException,則IllegalArgumentException通常會添加信息值。

所以在我執行錯誤記錄的頂級代碼中,我必須執行以下操作:

catch (RuntimeException re)
{
   logError(getTheOutermostUsefulException(re));
}

/** 
 * heuristics to tease out whether an exception
 * is wrapped just for the heck of it, or whether
 * it has informational value
 */
Throwable getTheOutermostUsefulException(RuntimeException re)
{        
   // subclasses of RuntimeException should be used as is
   if (re.getClass() != RuntimeException)
      return re;
   // if a runtime exception has a message, it's probably useful
   else if (re.getMessage() != null)
      return re;
   // if a runtime exception has no cause, it's certainly
   // going to be more useful than null
   else if (re.getCause() == null)
      return re;
   else
      return re.getCause();
}

這種理念適合我,但實施感覺很糟糕。 有沒有更好的方法來處理包裝異常?


相關問題:

Spring是我所知道的唯一一個相對類似的庫。 它們具有嵌套異常: NestedRuntimeExceptionNestedCheckedException 這些異常具有有用的方法,例如getMostSpecificCause()contains(Class exType) 他們的getMessage()方法返回cause的消息(如果包裝異常已經有消息,則會附加它)。

它用在Spring的數據訪問異常層次結構中。 這個想法是每個數據庫供應商在其JDBC驅動程序中公開不同的異常。 Spring捕獲它們並將它們轉換為更通用的DataAccessExceptions 這樣做的另一個好處是,已檢查的異常會自動轉換為運行時異常。

話雖如此,代碼並不是很復雜,我相信你可以在你的代碼庫中做類似的事情。 不需要為此添加Spring依賴項。

如果我是你,我不會試圖過早地“解開”異常,除非你真的可以在那時和那里處理它們。 我會讓它們冒泡(包裝或不包裝),使用全局ExceptionHandler查看其因果鏈,找到第一個“有意義”的異常,並提取智能錯誤消息。 如果無法這樣做,我只會打印“技術錯誤”並在錯誤消息的詳細信息或某種日志中添加整個堆棧跟蹤,以便進行錯誤報告。 然后,您將修復錯誤和/或拋出更有意義的異常。

Guava的Throwables.getCausalChain()也可能有助於簡化異常處理:

Iterables.filter(Throwables.getCausalChain(e), IOException.class));

編輯:

我更多地考慮了你的問題,我認為你不應該真的擔心用特定的“WrapperException”類型包裝你的異常。 您應該使用最有意義的東西來包裝它們:要么是簡單的RuntimeException (Guava的Throwables.propagate()可能在那里有用),帶有附加錯誤消息的RuntimeException ,或者適當時更有意義的異常類型。

Java的因果鏈機制無論如何都會讓你找到根本原因。 您不必擔心代碼中的異常包裝。 編寫一個全局異常處理程序來管理它,並在其他地方使用標准異常冒泡。

通常,最好避免使用未經檢查的異常包裝已檢查的異常(請參閱此處此處 )。 一些解決方法:

  1. 使用Callable而不是Runnable
  2. 使用像Bohemian建議的Executors框架
  3. Subclass Runnable (或者你正在使用的任何接口/類)並添加一些方法來檢查事后運行期間的異常,可能是一個方法public Exception getRunException()或類似的。

如果你必須包裝一個已檢查的異常,我認為最好的方法就是你已經這樣做了,定義一個RuntimeException的子類。 但是如果可能的話,我會盡量避免它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM