Saturday, August 10, 2013

Spring MVCでアスペクト(インターセプター)を実装する

Spring MVCでアスペクト(インターセプター)を実装するのは結構簡単。アスペクトっていうのは、要するにメソッドに対して前後処理をするモジュールのこと。フィルターみたいなやつですね。Springの場合はSpring配下にある任意のクラスのメソッドに適応可能。アスペクトで実装する一番ありがちな処理はログでしょうね。私もそのために使いました。あとはエラー処理とか。

SpringではどうもAspect Jを使ってAOPをしていて、用語もそれに従っている。ここでは面倒なので細かい用語の説明はなし。気になる人はアドバイス、ポイントカット、ジョインポイントあたりについて調べるといいんじゃないでしょうか。もっと色々ありそうですけど。

アスペクトの種類

アスペクトには以下の種類がある。

  • Before : メソッドの処理の前
  • AfterReturning : メソッドの処理の後例外は除く)
  • AfterThrowing : メソッドで例外が発生したとき
  • After : メソッドの処理の後(例外も含む)
  • Around: メソッドの前後処理を記述
単純なログならBeforeやAfter*、処理時間の計測とかを含むならAround、っていうのがありがちのようですね。

アスペクトのクラスを実装

すごい簡単。アスペクトのクラスに@Aspect、インターセプターの処理のメソッドに、種類に応じて上記の名前のアノテーション(@Beforeとか)をつけるだけ。

例えば、Service層のクラスの中で、自作の@UpdateDatasourceというアノテーションのついているメソッドのみ特定のログを出力したいときの例。ここで、サービス層はorg.opentechnica.sample.serviceパッケージにあるとする。

アノテーションは普通に実装。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface UpdateDatasource {
    public String value();
}

アスペクトも、アノテーション以外は普通に実装。

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(public * org.opentechnica.sample.service..*(..))")
    public void before(JoinPoint jp) {
        try {
            Class targetClass = jp.getTarget().getClass();
            String methodName = jp.getSignature().getName();
            MethodSignature signature = (MethodSignature) jp.getSignature();
            Method method = signature.getMethod();

            UpdateDatasource updateDatasourceAnnotation = method
                    .getAnnotation(UpdateDatasource.class);
            if (updateDatasourceAnnotation != null) {
                Logger logger = Logger.getLogger(targetClass);
                logger.info("before");
            }
        } catch (SecurityException e) {
            // do nothing
        }
    }
}

@Beforeアノテーション

@Beforeアノテーションでメソッドの実行前にインジェクションする処理であることを表している。アノテーションの引数("execution(public * org.opentechnica.sample.service..*(..))")は、この処理を実行する対象をフィルタリングする条件。ポイントカットっていやつですね。ここでの指定は以下のような意味。

  • publicで
  • 任意の戻り値型で
  • org.opentechnica.sample.serviceパッケージ以下の任意のクラスの
  • 任意の引数を持つ

対象のメソッドが目的のアノテーションを持つかどうか調べる

Javaは同一のメソッド名でも引数が違うと違うシグニチャになるので普通のリフレクションだと面倒だけど、beforeメソッドに渡されるJoinPointから、以下のようMethodSignatureにキャストすると直接取得できる。

MethodSignature signature = (MethodSignature) jp.getSignature();
Method method = signature.getMethod();
で、methodがとれたらもうmethod.getAnnotation(UpdateDatasource.class)とすれば、自作の@UpdateDatasourceが付加されているかどうかわかる。

applicationContext.xmlでAOPの設定

アスペクトを実装したら、applicationContext.xmlに以下の一行を追加するだけで動く。便利!

<aop:aspectj-autoproxy/>

No comments: