AspectJに割りと最近のバージョンからかけるようになったAnnotationを使った記法がある。
これが、意外と情報が少なめってことで自分用のメモ
アスペクトの定義
まずは基本のアスペクトの定義から。これは、普通のクラスにAspectアノテーションをつけるだけでOK。
package kazuki;
import org.aspectj.lang.annotation.Aspect;
@Aspect public class SimpleAspect {
} |
ポイントカットの定義
ポイントカットの定義は、戻り値がvoidのpublicなメソッドにPointcutアノテーションをつけることで出来る。
Pointcutアノテーションの括弧の中に昔からある"execution(* *.*(..))"みたいな定義を書けばOK
package kazuki;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;
@Aspect public class SimpleAspect?{ @Pointcut("execution(* *.*(..))") ????public void somePointcut() {
????} } |
アドバイスの定義
あとは適切なアドバイスさえ書けばアスペクト指向できちゃう。
アドバイスの定義もアノテーション付きのメソッドでOK。
代表的なのに下のようなのがある
- Before:前
- After:後
- Around:周囲?
- AfterReturning:return後
- AfterThrowing:throw後
例えば、全てのメソッドが呼ばれる前にsample adviceと呼ばれるようにするには下のように書けばOK
package kazuki;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;
@Aspect public class SimpleAspect { @Pointcut("execution(* *.*(..))") ????public void somePointcut() {
????}
????@Before("somePointcut()") ????public?void someAdvice() { ????????// ここに処理を書く ????????System.out.println("sample advice"); ????} } |
JoinPoint/ProceedingJoinPoint
Aroundを使うと、あるメソッドの処理とかをまるごと横取りすることができる。
その際に、もとの処理を呼び出すためにProceedingJoinPointってのを使う。 因みに、別にもとの処理を呼び出す必要がないときは普通のJoinPointを使う。 これらのクラスを使うと、メソッドの引数や名前やアスペクトを適用したクラスのインスタンスなんかが取れたりする。
早速使ってみた。
package kazuki;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut;
@Aspect public class SimpleAspect?{ @Pointcut("execution(* *.*(..))") ????public?void somePointcut() {
????}
????@Before("somePointcut()") ????public void someAdvice(JoinPoint thisJoinPoint) { ??????System.out.println(thisJoinPoint.getThis()); // Object ??????System.out.println(thisJoinPoint.getArgs()); // Object[] ????} } |
今まで動作させてなかったけど、このアスペクトを組み込んでハローワールドを動かすとどういう結果になるかをやってみた
package kazuki;
public class Main { ??public static void main(String[] args) { ????System.out.println("Hello world"); ??} } |
実行すると下のような結果になる。getThisはstaticメソッドなのでnullになってる。
null
[Ljava.lang.Object;@e89b94
Hello world
Hello worldをちょっと大げさにインスタンスメソッドを使うように改造してみた。
package kazuki;
public class Main?{ ??public static void main(String[] args) { ????new Main().sayHello(); ??}
??private void sayHello() { ????System.out.println("Hello world"); ??} } |
これを実行すると下のようになる。sayHelloメソッドの呼び出しの時にはgetThisはnullじゃなくてちゃんとしたインスタンスが返ってきている。
null
[Ljava.lang.Object;@e89b94
kazuki.Main@13e205f <== コイツがsayHello呼び出しの時のgetThis
[Ljava.lang.Object;@1bf73fa
Hello world
ほかにも何個かメソッドがあるけど、必要に応じて使っていけばいいと思う。
さて、もう1つのJoinPointのProceedingJoinPointを試してみようと思う。
これは、Aroundアドバイスで良く使う。 Aroundは、対象となる処理をまるまる横取りするような動きをする。なので、もとの処理を呼び出さないと完全にもとの処理が実行されない!
ためしにログを吐くようなアスペクトを作ってみた。
package kazuki;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;
@Aspect public class SimpleAspect { @Pointcut("execution(* *.*(..))") ??public void somePointcut() {
??}
??@Around("somePointcut()") ??public Object log(ProceedingJoinPoint thisJoinPoint) throws Throwable { ????try { ??????System.out.println("BEGIN: " + thisJoinPoint.getSignature().getName()); ??????return thisJoinPoint.proceed(); // ここで本来の処理を呼び出してる ????} finally { ??????System.out.println("END: " + thisJoinPoint.getSignature().getName()); ????} ??} } |
このアスペクトを適用して、さっきのHello worldを実行すると下のような結果になる。メソッドの呼び出しが漏れなくトレースできてる。
BEGIN: main
BEGIN: sayHello
Hello world
END: sayHello
END: main
とりあえず今日はここまで。
本当に基本的だけど、基本だからこそよく使うあたりだと思う。ポイントカットの定義[コード内でのexecution(* *.*(..))の部分)]の書き方はAspectJのドキュメントに載ってる!はず!!(英語だけど)
そこをマスターすれば、ここにあることだけで結構できるんじゃないかな。