web-dev-qa-db-ja.com

メソッドロギングのインターセプターとしてのカスタムアノテーション

Javaの達人、

私はannotationsが初めてなので、これをあまり検索していません。

メソッド呼び出しをinterceptするCustom Annotationを実装したいと思います。非常に基本的なものから始めるには、メソッド名とパラメーターを出力するだけで、loggerステートメントを回避できます。

このようなサンプル呼び出し:

public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
    log.debug("in findMyAppObjectById(" + id + ")");
    //....
}   

以下に変換できます:

@LogMethodCall(Logger.DEBUG)
public MyAppObject findMyAppObjectById(Long id) throws MyCustomException {
    //....
}   

これについてのヒントを得ることができますか?

23
SiB

私のコメントの回答に基づいて、あなたはこれを注釈だけで行うことはできません。もちろん、アノテーションを作成し、それを検出して反射するコードを作成し、コードを実行できますが、呼び出す前にparserメソッドを呼び出す必要があるため、コードをあまり変更しませんあなたのメソッドと私はあなたが各呼び出しの前にパーサーメソッドを呼び出す必要があるので、あまりあなたを助けないと思います。

言及した動作(自動呼び出し)が必要な場合、Spring(プレーンJava)やAspectJ(AspectJコード)などのAOPフレームワークと注釈を組み合わせる必要があります。その後、ポイントカットを設定し、このポイントに到達するたびに、いくつかのコードが実行される場合があります。メソッド実行の前後にコードを実行するように構成できます。

最初のシナリオで十分であれば、次のようなことができます。

ロガー:enum

public enum Logger {
    INFO,
    DEBUG;
}

LogMethodCall:アノテーション

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

@Retention( RetentionPolicy.RUNTIME ) // the annotation will be available during runtime
@Target( ElementType.METHOD )         // this can just used in methods
public @interface LogMethodCall {

    Logger logLevel() default Logger.INFO;

}

Person:注釈付きクラス

public class Person {

    // will use the default log level (INFO)
    @LogMethodCall
    public void foo( int a ) {
        System.out.println( "foo! " + a );
    }

    @LogMethodCall( logLevel = Logger.DEBUG )
    public void bar( int b ) {
        System.out.println( "bar! " + b );
    }

}

Utils:log静的メソッドを使用したクラス(これは「解析」を実行します)

public class Utils {

    public static void log( Object o, String methodName ) {

        // gets the object class
        Class klass = o.getClass();

        // iterate over its methods
        for ( Method m : klass.getMethods() ) {

            // verify if the method is the wanted one
            if ( m.getName().equals( methodName ) ) {

                // yes, it is
                // so, iterate over its annotations
                for ( Annotation a : m.getAnnotations() ) {

                    // verify if it is a LogMethodCall annotation
                    if ( a instanceof LogMethodCall ) {

                        // yes, it is
                        // so, cast it
                        LogMethodCall lmc = ( LogMethodCall ) a;

                        // verify the log level
                        switch ( lmc.logLevel() ) {
                            case INFO:
                                System.out.println( "performing info log for \"" + m.getName() + "\" method" );
                                break;
                            case DEBUG:
                                System.out.println( "performing debug log for \"" + m.getName() + "\" method" );
                                break;
                        }

                    }
                }

                // method encountered, so the loop can be break
                break;

            }

        }

    }

}

AnnotationProcessing:注釈処理をテストするためのコードを持つクラス

public class AnnotationProcessing {

    public static void main(String[] args) {

        Person p = new Person();
        Utils.log( p, "foo" );
        p.foo( 2 );
        Utils.log( p, "bar" );
        p.bar( 3 );

    }
}

もちろん、ニーズに合わせてコードを改善する必要があります。これは単なる出発点です。

注釈の詳細:

AOPの詳細:

37
davidbuzatto

すでに提案したように、AOPと注釈が最適なオプションです。 jcabi-aspects (私は開発者です)から既製のメカニズムを使用することをお勧めします。

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

すべてのメソッド呼び出しはSLF4Jに記録されます。

3
yegor256