さて、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
今、それはずっと良く見えます。:)))
楽しみ!