web-dev-qa-db-ja.com

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フープラ全体で自動的にラップします。

私はこれを行う簡単な方法を高低で検索しましたが、何も見つかりませんでした。わからないけど、注釈が付いた木すべての森が見えないのかもしれません。

私がそのような注釈をどのように実装するかについてのポインタがあれば、大歓迎です。

30
TroutKing

これを行うには、メソッドの周りにプロキシを使用するAOPフレームワークが必要になります。このプロキシは例外をキャッチし、finallyブロックを実行します。正直なところ、AOPをサポートするフレームワークをまだ使用していない場合、これらの数行を保存するためだけに使用するかどうかはわかりませんodコード。

ただし、次のパターンを使用して、よりエレガントな方法でこれを行うことができます。

_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";                
    });
}
_
21
JB Nizet

動的プロキシを使用してこれを実装できます。設定には少し時間がかかりますが、一度設定すれば、かなり簡単です。

まず、インターフェイスを定義し、インターフェイスに注釈を配置します。

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
}
17
Kal

自分で注釈と注釈プロセッサを実装し、コンパイル(javac -processor)。その他の方法は、AOPを使用することです。たとえば、AspectJまたはSpring AOP(Springを使用する場合)です。

3
korifey

afaikは、@TryCatchWithLoggingAndCleanUpアノテーションの各メソッド呼び出しを監視する必要があり、これは非常に面倒です。基本的に、リフレクションによって各メソッドのアノテーションを取得してから、例外処理とロギングを行うことができます。しかし、あなたがそれをしたいと思うかどうかはわかりません。

0
Flowqi Schmowqi