web-dev-qa-db-ja.com

タイミングメソッド呼び出し用のJavaアノテーションの記述

メソッド呼び出しの時間を回すJavaアノテーションを書きたい。次のようなもの:

@TimeIt
public int someMethod() { ... }

このメソッドが呼び出されると、このメソッドにかかった時間をコンソールに出力する必要があります

私はそれをPythonで行う方法を知っています、これは私がそれをしたいことです:

from time import time, sleep

def time_it(func):
    def wrapper(*args, **kwargs):
        start = time()
        func(*args, **kwargs)
        stop = time()
        print "The function", func.__name__, " took %.3f" % (stop - start)
    wrapper.__name__ = func.__name__
    return wrapper

@time_it
def print_something(*args, **kwargs):
    print "before sleeping"
    print args, kwargs
    sleep(3) # wait 3 seconds
    print "after sleeping"

print_something(1, 2, 3, a="what is this?")

だから私の質問は?このようなものを書くためのドキュメントはどこにありますか? apt ドキュメントを試しましたが、うまくいきませんでした。誰かがこのようなものを書くのを手伝ってくれる?

26
roopesh

AFAIK、Tomaszは、これは注釈を使用して行うことはできないと言っています。混乱は、PythonデコレータとJavaアノテーションは同じ構文を共有しますが、提供する動作の点で完全に異なるという事実から生じていると思います!

アノテーションは、クラス/メソッド/フィールドに添付されたメタデータです。 このブログ投稿 は、AOPを使用したタイミング手法のポイントに対処します。 Springを使用していますが、基本的な前提は同じです。 AOPコンパイラを使用するのが良ければ、コードを変換することはそれほど難しくありません。別の参照(春固有) ここ

[〜#〜] edit [〜#〜]:本格的なプロファイラーを使用せずに、アプリケーションの全体的なメソッドタイミングを設定することが目的の場合は、 hprof を使用して、合計実行統計を収集できます。

10

簡単に言えば、できません!

アノテーションは、コードと一緒に自動的に開始されるコードではなく、単なるアノテーションであり、コードの読み込みや実行など、コードを操作する他のプログラムが使用できる情報です。

必要なのはAOP:アスペクト指向プログラミングです。

7
Tomasz Stanczak

2016年の時点で、気の利いたアスペクトアノテーションライブラリがあります jcabi-aspects

ドキュメントから:

メソッドに@Loggableアノテーションを付けると、メソッドが呼び出されるたびに、SLF4Jロギング機能が実行の詳細と合計実行時間を含むメッセージを受け取ります。

public class Resource {
  @Loggable(Loggable.DEBUG)
  public String load(URL url) {
    return url.openConnection().getContent();
  }
}

次のようなものがログに表示されます。

[DEBUG] #load('http://www.google.com'): returned "<html ..." in 23ms

@Loggableの詳細を読む ここ

5
headsvk

Coda Hale Metrics ライブラリを確認してください。この機能を提供するメソッドに@Timedアノテーションを提供します。あなたがそれをしている間にチェックしてください Code Hale Dropwizard それが彼らのサービスフレームワークにどのように統合されたかの例があります。

@GET
@Timed
public Saying sayHello(@QueryParam("name") Optional<String> name) {
    return new Saying(counter.incrementAndGet(),
                      String.format(template, name.or(defaultName)));
}
3
Jeff

すべての否定者にもかかわらず、あなたはこれを行うことができます。 Javaアノテーションは、操作するソースファイルまたはクラスファイルを変更できないため、オプションは次のとおりです。

1)スーパークラスを使用します。注釈プロセッサは、抽象メソッドを実行するスーパークラスを生成できます。実際のクラスはこのメソッドを実装します。欠点は、スーパークラスが実装を提供できるように、時間を計りたいメソッドの名前を変更する必要があることです。結果は次のようになります

@BenchmarkMe( extend="MySuperClass" )
public class MyClass extends BenchmarkMyClass {
    public void normalMethod() { ... }
    public void bench_myMethod() { ... }
}  

と注釈プロセスは生成されます:

public class BenchmarkMyClass extends MySuperClass {
    public abstract void bench_myMethod();
    public void myMethod() {
       benchmarkStart();
       try {
          bench_myMethod();
       } finally { benchmarkStop(); }
    }
}

命名規則を使用して、私の例では「bench_」という接頭辞が使用されたため、どのメソッドにタイミングをとるべきかを示します。

2)ClassFileTranformerとアノテーションを使用するアプローチは、タイミングに関心のあるメソッドをマークするために使用できるランタイムアノテーションを作成することです。実行時に、ClassFileTransformerがコマンドラインで指定され、バイトコードを変換してタイミングコードを挿入します。

バイトコードの操作が好きでない限り、AOPを使用する方が適切ですが、[〜#〜] is [〜#〜]が可能です。

2
Simon G.

Java.lang.reflect.Proxyを誰も指摘していないことに驚いています。古いスレッドですが、この情報は誰かに役立つと思います。

プロキシは与える興味深いプロパティを持っています

  1. fooのプロキシインスタンスをtrueとして。
  2. 呼び出しハンドラーにメソッドを含めることができます。このメソッドは、最初に時間を出力してから、オブジェクトから実際のメソッドを起動します。

オブジェクトに何らかのインターフェースを実装させることで、すべてのオブジェクトにこのプロキシを設定するか、Comparableを使用できます。

動的プロキシのセクションをデコレータとして探します。

http://www.ibm.com/developerworks/library/j-jtp08305/

2
Ganesh Bhat

同じことを何度か思いつき、次のように書き始めました。

注釈:

package main;
import Java.lang.annotation.ElementType;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
import Java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Clocking {

}

オブジェクトのインターフェース:

package main;

public interface Examples {
    @Clocking
    void thisIsAMethod();

    void thisIsAnotherMethod(String something);

    @Clocking
    void thisIsALongRunningMethod();
}

呼び出しハンドラ:

package main;
import Java.lang.reflect.InvocationHandler;
import Java.lang.reflect.Method;
import Java.time.Duration;
import Java.time.Instant;

public class ExamplesInvocationHandler implements InvocationHandler {
    // ******************************
    // Fields
    // ******************************
    private Examples examples = new ExamplesImpl();

    // ******************************
    // Public methods
    // ******************************
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // If the annotation is not present, just redirect the method call to its Origin...
        if(!method.isAnnotationPresent(Clocking.class)) {
            return method.invoke(examples, args);
        }

        // ... otherwise log the execution time of it.
        Instant start = Instant.now();
        Object returnObj = method.invoke(examples, args);
        Instant end = Instant.now();

        // TODO: This is for demonstration purpose only and should use the application's logging system.
        System.out.println("Method " + method.getName() + " executed in " + Duration.between(end, start) + ".");

        return returnObj;
    }

    // ******************************
    // Inner classes
    // ******************************
    private static class ExamplesImpl implements Examples {
        @Override
        public void thisIsAMethod() {
            System.out.println("thisIsAMethod called!");
        }

        @Override
        public void thisIsAnotherMethod(String something) {
            System.out.println("thisIsAnotherMethod called!");
        }

        @Override
        public void thisIsALongRunningMethod() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("thisIsALongRunningMethod called!");
        }
    }
}

最後にこれをテストするためのエントリポイント:

package main;
import Java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        Examples examples = (Examples) Proxy.newProxyInstance(Examples.class.getClassLoader(), new Class[]{Examples.class}, new ExamplesInvocationHandler());

        examples.thisIsAMethod();
        examples.thisIsAnotherMethod("");
        examples.thisIsALongRunningMethod();
    }
}

オブジェクトをインスタンス化するためにプロキシが必要であり、「一般的な既に記述された」コードには実際には使用できないため、これには改善が必要です。しかし、それはあなたをより完全な何かに導くかもしれません。

2
Romano

Javaではそれほど簡単ではありません。基本的なアイデアはこれです:

  1. 「このメソッドの時間」という注釈を作成します
  2. 次の目的でバイトコード変換を使用するJavaエージェントを作成します。a。アノテーション付きのメソッドを検索します。b。タイミングコードを追加します。
  3. Javaを実行するときにjavaagentオプションを設定して新しいエージェントを使用する

この記事はあなたを始めるでしょう: http://today.Java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

BTraceを使用してこれをさらに簡単にすることもできます。 http://kenai.com/projects/btrace/pages/Home

1
Dave

すでに述べたように、AOPまたはhprofはほとんどのニーズをカバーできませんが、主張する場合はJSR269を使用した回避策があります。参考までに、aptは廃止され、注釈処理APIとツールが1.6に組み込まれました(そして、それは刺激的な名前JSR269で呼び出されます)。

回避策は、_@TimeIt_アノテーションを持つメソッドを含むクラスを拡張するクラスを生成するアノテーションプロセッサを作成することです。この生成されたクラスは、timedメソッドをオーバーライドする必要があります。Python _time_it_のようになりますが、func(*args, **kwargs)という行はsuper.methodName(arg1, arg2, ...)

ただし、2つの注意点があります。

  1. コードの他の場所では、元のクラスではなく、生成されたクラスのインスタンスを作成する必要があります。まだ存在しないクラスを参照するため、これは問題です。最初の処理ラウンドの最後に作成されます。
  2. Javax.annotation.processingパッケージとjavax.lang.modelパッケージに精通する必要があります。これらは少し厄介な私見です。
0
Robert Bossy