AOPと注釈を使用してJavaログを記録する


時々、実行したメソッドごとに受信したパラメータ、戻り値、実行時間などの情報を記録したい(slf 4 jとlog 4 jを介して).AspectJ、jcabi-aspects、Java注釈の助けでこの考えを実現しました.

    public class Foo {
      @Loggable
      public int power(int x, int p) {
        return Math.pow(x, p);
      }
    }

log 4 jでは、次の出力が表示されます.

    [INFO] com.example.Foo #power(2, 10): 1024 in 12μs
    [INFO] com.example.Foo #power(3, 3): 27 in 4μs

かっこいいですよね?次に、それがどのように働いているかを見てみましょう.
注釈
注記はJava 6で採用されている技術の一つです.プログラムの実行に影響を与えないメタプログラミング命令であり、指定した要素(メソッド、クラス、変数)をマークすることができます.すなわち,注釈はコードに見られるタグである.いくつかの注釈はコンパイル段階でしか見られない--コンパイルされたものには存在しない.classファイルでは、コンパイル後も他の注釈が表示されます.
例えば、@Overrideは第1のタイプ(その保持タイプはSOURCE)であり、JUnitの@Testは第2のタイプ(保持タイプはRUNTIME),@Loggable--私が上で使用したのは第2の注釈であり、jcabi-aspectsに含まれ、コンパイル後に残る.classファイルにあります.
なお、上記のpower()メソッドは、注釈されてコンパイルされてもslf 4 jに何も送信されない.関連ソフトウェアに「私の実行プロセスを記録してください」と注意するタグにすぎないことを理解することが重要です.
AOP
AOP(カットオフプログラミング向け)は、ソースコードを明示的に変更せずに実行可能ブロックを追加できる技術である.上記の例では、実装クラスにメソッドの実行を記録するのではなく、power()メソッドの呼び出しを他のクラスでブロックし、実行時間を測定し、slf 4 jに送信する.
ブロッククラスが@Loggable注釈を認識しpower()メソッドの呼び出しを記録できるようにしたいのですが、もちろん他のメソッドもブロックできるはずです.
この考え方はAOPの出発点とよく一致している--複数のクラスで共通の機能を繰り返し実現することを避ける.
ログはJavaの主な機能の補完です.コードに煩雑なログ命令を加えたくないので、コードの可読性が低下するので、他の場所でこっそりログを記録したいと思っています.
AOPの観点から,我々の解決策は,所望の機能を実現するために,指定された切り込み点と周囲通知の断面を新規に作成することである.
AspectJ
次に、これらの不思議な注釈を見てみましょう.まず,jcabi−aspectsがどのようにAspectJを用いて注釈を実現したかを理解する.次は簡単な例ですJAvaですべてのコードが見つかりました.

    @Aspect
    public class MethodLogger {
      @Around("execution(* *(..)) && @annotation(Loggable)")
      public Object around(ProceedingJoinPoint point) {
        long start = System.currentTimeMillis();
        Object result = point.proceed();
        Logger.info(
          "#%s(%s): %s in %[msec]s",
          MethodSignature.class.cast(point.getSignature()).getMethod().getName(),
          point.getArgs(),
          result,
          System.currentTimeMillis() - start
        );
        return result;
      }
    }

この接面(aspect)にはaround()の通知があり、接面は@Aspectで注釈し、通知は@Aroundで注釈する.classファイルには、実行時に興味のあるオブジェクトにいくつかの情報を提供するタグが付けられています.
@Around注記にパラメータがあります.
  • 可視性は*(public、protectedまたはprivate)です.
  • 名前は*(どんな名前でもOK);
  • パラメータは..(任意のパラメータで可能);
  • 注記@Loggable
  • 通知はこの方法に適用されます.
    注記メソッドが呼び出されるとブロックされ、around()通知はブロックメソッドの前に実行され、@Aroundタイプの通知にはProceedingJoinPointクラスのインスタンスがパラメータとして必要であり、その後power()メソッドにオブジェクトが返されます.
    Power()メソッドを呼び出すには、join pointオブジェクトを呼び出すproceed()メソッドが必要であることを通知します.
    次に環境変数をコンパイルして追加し、メインファイルFooを作成します.classはそれを呼び出すことができます.今まですべてが順調で、私たちはまだ最後の一歩が必要です.通知を動かす必要があります.
    バイナリカット織り込み
    フェース織り込み(aspect waving)とは、ターゲットオブジェクトにフェースを適用して新しいエージェントオブジェクトを作成するプロセスです.カット織り込みはいくつかのコードを元のコードに挿入し、AspectJはこのようにします.バイナリJavaクラスFooを2つあげます.ClassとMethodLoggerclass; 修正されたFooの3つのクラスを返します.class、 Foo$AjcClosure1.Classと未修正のMethodLogger.class.
    異なる通知をどのように対応するどの方法に適用するかを理解するために、AspectJは織り込む.classファイルでは注釈が使用され、反射を使用して環境変数のすべてのクラスを参照します.@Around注記のどの方法が条件を満たすかを解析します.Power()が発見されました.
    上記の操作は2つのステップに分ける必要があります.まず、私たちは.JAvaファイルコンパイル.その後、AspectJはコンパイルされたファイルを織り込み/修正し、織り込まれたFooクラスは以下のように見えます.
    
        public class Foo {
          private final MethodLogger logger;
          @Loggable
          public int power(int x, int p) {
            return this.logger.around(point);
          }
          private int power_aroundBody(int x, int p) {
            return Math.pow(x, p);
          }
        }
    

    AspectJ織り込みは私たちの元の機能を新しい方法powerに移動します.AroundBody()では、すべてのpower()呼び出しを切面クラスMethodLoggerにリダイレクトします.
    次の図は、Power()を呼び出すたびに、次の手順を示します.
    図の中の小さな緑が元の方法power()です.
    ご覧のように、切面織り込み過程はクラスと切面などを結びつけています.織り込まれていない場合は、コンパイルされたバイナリコードと注釈の山にすぎません.
    jcabi-aspects
    jcabi-aspectsは、Loggable注記とMethodLogger接面を含むJARライブラリで、他の注記と接面もあります.環境変数に依存を加え、jcabi-maven-pluginを構成するだけで、自分でフェースを実装する必要はありません.Maven Centralで最新バージョンを取得できます.
    
        <project>
          <depenencies>
            <dependency>
              <dependency>
                <groupId>com.jcabi</groupId>
                <artifactId>jcabi-aspects</artifactId>
              </dependency>
              <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
              </dependency>
            </dependency>
          </depenencies>
          <build>
            <plugins>
              <plugin>
                <groupId>com.jcabi</groupId>
                <artifactId>jcabi-maven-plugin</artifactId>
                <executions>
                  <execution>
                    <goals>
                      <goal>ajc</goal>
                    </goals>
                  </execution>
                </executions>
              </plugin>
            </plugins>
          </build>
        </project>
    

    織り込みが複雑なので、Mavenプラグインとajc goalで便利な織り込みをしました.AspectJも直接使えますが、jcabi-maven-pluginをお勧めします.
    はい、今@comを使ってもいいです.jcabi.aspects.Loggable注記あなたの方法の実行過程はslf 4 jで記録されます.
    上記の操作で予期した効果が得られなかった場合は、Github issueに質問を歓迎します.
    関連記事
  • なぜLog 4 J
  • ではなくSLF 4 Jを使用するのか
  • 注目すべきいくつかのEclipseスーパークールプラグイン
  • 経典論文翻訳ガイドの『A Bloat-Aware Design for Big Data Applications』
  • 新刊書推薦:Eclipse 4 Plug-in Development by Example
  • 私はどのように0から始めて、23日の中で1種のAndroidゲームの開発の–Part 4–第9から第11日の
  • を完成します
  • Java異常の面接質問と答え-part 3
  • Java/Java EEスキル
  • の評価方法
  • Pmd、Findbugs、CheckStyleの分析結果
  • をよりよく利用する方法
  • Spring MVC+Hibernate+Maven:Crud動作例
  • Javaで二重チェックロックを使用して単例
  • を実装する方法