用于包装方法的Java注释

时间:2011-12-28 15:43:32

标签: java methods annotations

我有很多样板代码基本上遵循这种模式:

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箍包裹带注释方法的内容。

我一直在寻找一种直截了当的方法,但却没有找到任何东西。我不知道,也许我只是看不到所有注释树木的树林。

关于如何实现这样的注释的任何指针都将非常感激。

5 个答案:

答案 0 :(得分:20)

要做到这一点,你需要一些在你的方法周围使用代理的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";                
    });
}

答案 1 :(得分:16)

您可以使用动态代理来实现此功能。它需要一些设置,但一旦完成,非常简单。

首先,定义一个接口并将注释放在界面上。

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
}

答案 2 :(得分:3)

您可以自己实现注释和注释处理器,并在每次编译时使用仪器代码(javac -processor)。其他方法是使用AOP,比如AspectJ或Spring AOP(如果你使用Spring)。

答案 3 :(得分:0)

afaik你必须监视@TryCatchWithLoggingAndCleanUp注释的每个方法调用,这将非常繁琐。基本上你可以通过反射得到每个方法注释,然后进行异常处理和记录。但我不确定你会不会那样做。

答案 4 :(得分:0)

另一种方法是在构建后使用 javassist 处理类文件,您需要在类中搜索具有指定注释的方法。并添加用于在包装方法和原始方法之间调用的桥接方法。看起来像是在调用 bridgeMethod() -> wrapperMethod() -> originalMethod()。我做了一个简单的项目来实现这种方法。您可以参考https://github.com/eshizhan/funcwraps