2013-10-15 84 views
8

比方說,我有一種拋出某種異常的方法。拋出異常的代碼位於訪問外部服務的第三方庫中。我有幾個類可以完成外部服務的大量工作,並且有很多異常處理來處理潛在的問題。我遇到的問題是我可能有很多例外情況,但是如果有一個,我可能只需要執行幾個操作之一,並且有大量的try/catch塊。異常的類型甚至可能不相關,或者不同的方法可能拋出相同類型的異常,但根據拋出它的方法需要採取不同的操作。使用註解進行異常處理?

我在找的是一個註釋,它可以代替try/catch,並且只是在該方法出現異常時指定要採取的行爲。我知道Spring ApsectJ可以做這種事情,但我目前還不能輕鬆添加任何新的依賴關係或修改pom來調整現有的依賴關係。因此,我希望這可以通過自定義註釋完成。例如:

@Catcher(action=SomeEnum.SOME_ACTION) 
public void doSomething(ServiceObj obj) throws SomeException { 
    ExternalService.makeThingsHappen(obj); 
} 

我會假設一個單獨的類當然會處理異常。另外一個困難是我需要傳遞的ServiceObj。如果makeThingsHappen()失敗,我可能需要obj來執行其他操作。 action變量會告訴處理程序類如何處理obj。

這可以做到沒有嚴重muckery,或者我希望有可能不存在的東西?

+0

註釋不會自行添加行爲。他們是元數據。你需要爲它們提供一個解析器,如果它發現它們就可以添加該行爲。 –

+0

我會稱之爲輕微泥濘;它可以通過AOP或一些字節碼操作來處理。 –

回答

17

這應該是一個低級別的過程,並不意味着我們不能和當前級別有相同的事情,但它可能需要一堆代碼,並且會使系統複雜一點。 然而,我的建議會是這樣的(我希望我知道它是正確的),首先爲誰想要處理異常定義一個接口,就像這樣。

interface ExceptionHandler{ 
    void handleException(Throwable t); 
} 

然後爲用戶(API)提供註釋以標記其方法可能拋出一些異常。

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.ANNOTATION_TYPE) 
@interface Catch{ 
    public Class<? extends ExceptionHandler> targetCatchHandler(); 
    public Class<? extends Throwable> targetException() default Exception.class; 
} 


@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
@interface CatchGroup{ 
    public Catch[] catchers(); 
} 

接下來我們需要一個接口來開始調用可能拋出異常的方法,像這樣。

interface Caller{ 
    void callMethod()throws Throwable; 
} 

,那麼你需要誰照顧和管理執行的流程,並調用可能異常處理程序

class MethodCaller{ 
    /* 
    * @param isntance: instance which implemented the Caller interface 
    */ 
    public static void callMethod(Caller instance) 
     throws Exception { 
    Method m = instance.getClass().getMethod("callMethod"); 
    Annotation as[] = m.getAnnotations(); 
    Catch[] li = null; 
    for (Annotation a : as) { 
     if (a.annotationType().equals(CatchGroup.class)) { 
     li = ((CatchGroup) a).catchers(); 
     } 
     // for(Catch cx:li){cx.targetException().getName();} 
    } 
    try { 
     instance.callMethod(); 
    } catch (Throwable e) { 
     Class<?> ec = e.getClass(); 
     if (li == null) { 
     return; 
     } 
     for (Catch cx : li) { 
     if (cx.targetException().equals(ec)) { 
      ExceptionHandler h = cx.targetCatchHandler().newInstance(); 
      h.handleException(e); 
      break; 
     } 
     } 
    } 
    } 
} 

最後,讓我們有一些例子一個傢伙,它的工作對我非常好,這個很酷。 異常處理程序。

public class Bar implements ExceptionHandler{//the class who handles the exception 
    @Override 
    public void handleException(Throwable t) { 
    System.out.println("Ta Ta"); 
    System.out.println(t.getMessage()); 
    } 
} 

和方法調用者。

class Foo implements Caller{//the class who calls the method 
    @Override 
    @CatchGroup(catchers={ 
     @Catch(targetCatchHandler=Bar.class,targetException=ArithmeticException.class), 
     @Catch(targetCatchHandler=Bar.class,targetException=NullPointerException.class)}) 
    public void callMethod()throws Throwable { 
    int a=0,b=10; 
    System.out.println(b/a); 
    } 
    public static void main(String[] args) throws Exception { 
    Foo foo=new Foo(); 
    MethodCaller.callMethod(foo); 
    } 
} 

正如你看到的,用戶必須調用由callmethod()方法的方法,您也將省略Caller接口,並使用註解,因爲它需要大量額外的一類聲明的方法不止一種codez。 我希望我可以舉手。

2

感謝您的幫助,所有。我看着Spring AOP,但最終決定反對它。我使用了try/catch塊,但創建了一個處理程序,它被注入到每個類中,並將所有拋出的異常包裝到自己的異常類中,然後將其傳遞給處理程序。這有點類似於user2511414的建議,因爲有一個專門的處理程序,但我放棄了註釋。我有很多的try/catch塊,但至少我保留了大部分的處理邏輯。我的解決方案的情況下,快速綱要其他人發現這一點,這是一個有點模糊,但你仍然可以得到的一點:

public enum DoThisEnum { 
    DO_THIS, 
    DO_THAT, 
    DO_OTHER_THING; 
} 

public class MyException extends Exception { 

    private DoThisEnum doThis; 
    private MyObject dataObj; 

    //Constructor, overloaded to run super(originalException) or super() 
    //as well as taking doThis and dataObj as parameters 
    //Getters, setters, etc 
} 

public interface IExceptionHandler { 

    void handleException(MyException exception); 

} 

然後用一個具體的類,它MyException實現IExceptionHandler,讀出附加數據,並基於它執行操作。然後,每個可能拋出這樣的異常塊可以被捕獲,像這樣:

... 
try { 
    doSomething(Object data); 
} catch (SomeException e) { 
    handler.handleException(new MyException(e, DoThisEnum.DO_THAT, data)); 
    //Anything else to do, maybe return or return null, rethrow, etc. 
} 

現在大部分的基本事實是封裝在處理器和try/catch塊是最小的。處理程序可以記錄原始異常的堆棧跟蹤,也可以基於它來做其他事情,然後根據枚舉執行自己的操作。也許這不是一個完美的解決方案,但它在這裏工作得很好。