2011-12-28 85 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 
}