用于包装方法的 Java 注释

2022-09-01 05:21:37

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

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/final部分始终相同。

我脑海中的注释会自动将注释方法的内容与整个hoopla包装在一起。try...catch...finally

我已经搜索了高处和低处,寻找一种直接的方法来做到这一点,但什么也没找到。我不知道,也许我只是看不到所有带注释的树木的树林。

任何关于我如何实现这种注释的指示将不胜感激。


答案 1

为此,您需要一些AOP框架,该框架将在您的方法周围使用代理。此代理将捕获异常并执行最终块。坦率地说,如果你还没有使用支持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;
}

从而避免了使用泛型并从可调用中返回 null 的需要。Callable<Void>

编辑:如果你想从你的方法返回一些东西,那么使方法通用。下面是一个完整的示例: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";                
    });
}

答案 2

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

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

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
}