2011-12-28 84 views
22

我有很多的樣板代碼,基本上遵循這樣的模式:的Java註釋用於包裝方法

function doSomething() { 
    try { 
    [implementation] 
    [implementation] 
    [implementation] 
    [implementation] 
    } catch (Exception e) { 
    MyEnv.getLogger().log(e); 
    } finally { 
    genericCleanUpMethod(); 
    } 
} 

我喜歡創建自己的註解清理我的代碼了一下:

@TryCatchWithLoggingAndCleanUp 
function doSomething() { 
    [implementation] 
    [implementation] 
    [implementation] 
    [implementation] 
} 

方法簽名變化很大(取決於方法的實際實現),但樣本try/catch/finally部分總是相同的。

我想到的註釋會自動將註釋方法的內容與整個try...catch...finally混淆。

我搜索了一個簡單的方法來做到這一點,但沒有找到任何東西的高和低。我不知道,也許我無法看到所有註釋樹木的樹林。

任何關於如何實現這樣的註釋的指針將不勝感激。

+0

對我來說這似乎不需要註釋;你可以只傳遞定義'implementationOfDoSomething()'和(可能的話)'genericCleanUpMethod()'的實現作爲'doSomething()'的參數,在try/catch/finally中調用它們,然後調用'doSomething ()'什麼時候需要模板邏輯? – 2011-12-28 15:46:49

+1

你的答案在於AOP,你研究過這個嗎? – smp7d 2011-12-28 15:49:29

+0

我的理解是他有同樣的try/catch /最終跨越多種方法。所以不只是一個doSomething,而更像doSomething1,doSomething2,......所有這些都是同樣的try/catch/finally,他想要提取到註釋 – jeff 2011-12-28 15:50:04

回答

19

爲此,您需要一些AOP框架,它可以在您的方法中使用代理。該代理將捕獲異常並執行finally塊。坦率地說,如果你沒有使用支持AOP的框架,我不確定我會只使用一個來保存這幾行代碼。

你可以用下面的方式來做到這一點更優雅的方式,雖然:

public void doSomething() { 
    logAndCleanup(new Callable<Void>() { 
     public Void call() throws Exception { 
      implementationOfDoSomething(); 
      return null; 
     } 
    }); 
} 

private void logAndCleanup(Callable<Void> callable) { 
    try { 
     callable.call(); 
    } 
    catch (Exception e) { 
     MyEnv.getLogger().log(e); 
    } 
    finally { 
     genericCleanUpMethod(); 
    } 
} 

我只是用Callable<Void>作爲一個接口,但你可以定義自己的Command接口:

public interface Command { 
    public void execute() throws Exception; 
} 

,從而避免需要使用通用Callable<Void>並從Callable返回null。

編輯:如果你想從你的方法返回的東西,然後使logAndCleanup()方法通用。這裏有一個完整的例子:

public class ExceptionHandling { 
    public String doSomething(final boolean throwException) { 
     return logAndCleanup(new Callable<String>() { 
      public String call() throws Exception { 
       if (throwException) { 
        throw new Exception("you asked for it"); 
       } 
       return "hello"; 
      } 
     }); 
    } 

    public Integer doSomethingElse() { 
     return logAndCleanup(new Callable<Integer>() { 
      public Integer call() throws Exception { 
       return 42; 
      } 
     }); 
    } 

    private <T> T logAndCleanup(Callable<T> callable) { 
     try { 
      return callable.call(); 
     } 
     catch (Exception e) { 
      System.out.println("An exception has been thrown: " + e); 
      throw new RuntimeException(e); // or return null, or whatever you want 
     } 
     finally { 
      System.out.println("doing some cleanup..."); 
     } 
    } 

    public static void main(String[] args) { 
     ExceptionHandling eh = new ExceptionHandling(); 

     System.out.println(eh.doSomething(false)); 
     System.out.println(eh.doSomethingElse()); 
     System.out.println(eh.doSomething(true)); 
    } 
} 

編輯:和與Java 8,包裝的代碼可以稍加整理:

public String doSomething(final boolean throwException) { 
    return logAndCleanup(() -> {     
     if (throwException) { 
      throw new Exception("you asked for it"); 
     } 
     return "hello";     
    }); 
} 
+0

+1 - 這就是我對這個問題的評論。 – 2011-12-28 16:20:24

+0

這與我自己的「B計劃」解決方案非常相似,註釋最終不切實際。這似乎是這種情況。非常感謝! – TroutKing 2011-12-28 16:33:18

+0

謝謝你,如果你沒有返回任何東西,效果很好,但是如果'callable'需要返回一些東西。您是否必須創建一個'ExecutorService'如下所示:http://stackoverflow.com/a/5516955/293280 – 2014-07-18 21:49:51

0

afaik您將不得不監視每個方法調用@TryCatchWithLoggingAndCleanUp註釋,這將是非常乏味的。基本上你可以通過反射來獲取每個方法的註釋,然後進行異常處理和日誌記錄。但我不知道你會想這樣做。

3

您可以在編譯時(javac -processor)每次都自行實現註釋和註釋處理器。其他方式是使用AOP,比如AspectJ或Spring AOP(如果使用Spring)。

11

你可以使用動態代理來實現這一點。它需要一些設置,但一旦完成,就非常簡單。

首先,定義一個接口並將註釋放置在接口上。

public interface MyInterface { 
    @TryCatchWithLogging 
    public void doSomething(); 
} 

現在,當你想要提供接口給消費者的實現,不提供他的實際執行,而是代理給它。

MyInterface impl = new java.lang.reflect.Proxy.newProxyInstance(
         Impl.class.getClassLoader(), 
         Impl.class.getInterfaces(), YourProxy(new Impl()); 

然後執行YourProxy。

public class YourProxy implements InvocationHandler { 
.... 

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
     if (method.isAnnotationPresent(TryCatchLogging.class)) { 
       // Enclose with try catch 
}