2

Spring AOP (または AspectJ) で再入可能なアスペクトを作成することは可能ですか?

次に例を示します。

@Log
public int calcFibonacci(int n) {
    if(n <= 1) {
        return n;
    } else {
        return calcFibonacci(n - 1) + calcFibonacci(n - 2);
    }
}

そしてアスペクト:

@Aspect
public class LoggingAspect {

@Around("@annotation(log)")
public Object measure(ProceedingJoinPoint pjp, Log log) throws Throwable {
    // log some relevant information from log annotation and pjp
    ...
    return pjp.proceed();
}

}

ここで、calcFibonacci が呼び出された回数を知りたいと思います (繰り返し呼び出しをカウントします)。

これを達成する方法はありますか?

4

2 に答える 2

3

さて、Spring AOP でこれをエレガントに解決することはできません。Andrei Stefanの回答に対する私の最初のコメントを参照してください。AOP で、アプリケーション コードがアスペクトの存在を認識したり、アスペクト関連のコードを呼び出したりする必要がある場合、これは不適切な設計であり、アンチ AOP です。したがって、ここに AspectJ ソリューションがあります。

execution()まず第一に、AspectJ には単なるポイントカット以上のものがありcall()ます。したがって、 によって注釈が付けられたジョインポイントをカウントするだけ@Logで、 への再帰呼び出しの実際の数の 2 倍の結果が得られcalcFibonacci(int)ます。このため、ポイントカットは

@annotation(log)

しかし

execution(* *(..)) && @annotation(log)

実際には、これでもまだ十分ではありません。複数のメソッドに@Logアノテーションが含まれている場合はどうなるでしょうか。それらの呼び出しをすべてカウントする必要がありますか? いいえ、する人だけcalcFibonacci(int)!したがって、「フィボナッチ コール カウンター」をさらに次のように制限する必要があります。

execution(* *..Application.calcFibonacci(int)) && @annotation(log)

以下は、完全にコンパイル可能なサンプル コードです。

注釈:

package de.scrum_master.app;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Log {}

再帰的フィボナッチ法による適用:

package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        int fibonacciNumber = 6;
        System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
    }

    @Log
    public int calcFibonacci(int n) {
        return n <= 1 ? n : calcFibonacci(n - 1) + calcFibonacci(n - 2);
    }
}

アスペクト、バージョン 1:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import de.scrum_master.app.Log;

@Aspect
public class LoggingAspect {
    int count = 0;

    @Before("execution(* *..Application.calcFibonacci(int)) && @annotation(log)")
    public void measure(JoinPoint thisJoinPoint, Log log) {
        System.out.println(thisJoinPoint + " - " + ++count);
    }
}

出力、バージョン 1:

execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 1
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 2
(...)
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 24
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 25
Fibonacci #6 = 8

では、フィボナッチ法を 2 回呼び出すとどうなるでしょうか。

int fibonacciNumber = 6;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
fibonacciNumber = 4;
System.out.println("Fibonacci #" + fibonacciNumber + " = " + new Application().calcFibonacci(fibonacciNumber));
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 1
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 2
(...)
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 24
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 25
Fibonacci #6 = 8
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 26
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 27
(...)
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 33
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 34
Fibonacci #4 = 3

ええとああ!!!

呼び出しの間にカウンターをリセットする (また、a などを使用して全体がスレッドセーフであることを確認するThreadLocal) か、ここで使用するシングルトン アスペクトの代わりに、制御フローごとにアスペクト インスタンス化を使用する必要があります。それの楽しみのために:

アスペクト、バージョン 2:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

import de.scrum_master.app.Log;

@Aspect("percflow(execution(* *.calcFibonacci(int)) && !cflowbelow(execution(* *.calcFibonacci(int))))")
public class LoggingAspect {
    int count = 0;

    @Before("execution(* *.calcFibonacci(int)) && @annotation(log)")
    public void measure(JoinPoint thisJoinPoint, Log log) {
        System.out.println(thisJoinPoint + " - " + ++count);
    }
}

ノート:

  • *ソースコードを読みやすくするために、パッケージとクラスの仕様を短くしました。de.scrum_master.app.Application同様のクラス/メソッド名との衝突を避けるために、またはその省略形を使用することもできます。
  • 注釈には、 「@Aspectフィボナッチ メソッドの実行ごとに 1 つのインスタンスを作成しますが、再帰的なものは除外します」というパラメーターが含まれるようになりました。

出力、バージョン 2:

execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 1
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 2
(..)
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 24
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 25
Fibonacci #6 = 8
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 1
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 2
(..)
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 8
execution(int de.scrum_master.app.Application.calcFibonacci(int)) - 9
Fibonacci #4 = 3

今、それはずっと良く見えます。:)))

楽しみ!

于 2014-06-23T08:03:07.190 に答える