ぺーぺーSEのブログ

備忘録・メモ用サイト。

Spring AOP

AOPの簡易理解は「元のソースコードに変更を加えずに新たな処理を追加する(挟み込む)こと」でよいか。
ここではSpring2.0以降のことを書いている(つもり)。


言葉の定義
  • Aspect
    • 横断的な関心事が持つ振る舞い(処理のこと)と、いつ振る舞いを適用するかをまとめたもの。
    • 以下に記述するAdviceとPointcutをまとめたものをAspectという。
  • Joinpoint
  • Pointcut
    • コード上にあるJoinpointの集合から、処理を織り込むたい場所の絞り込みを行った部分集合。
    • 「add」ではじまるメソッドが実行された時だけだとすると、条件を「add*」のように絞り込む。
  • Advice
    • Joinpointで実行される処理。
  • Intercepter
  • Advisor
    • AdviceとPointcutの両方を兼ね備えた、アスペクトをモジュール化したもの。

以降、「Aspect」、「Pointcut」、「Advice」のbean定義ベースの設定例、最後にアノテーションの利用について書く。

Aspect

bean定義例
<aop:config>
  <aop:aspect id="aspect" ref="adviceClass">
    ...
  </aop:aspect>
</aop:config>

<!-- Adviceを実装したクラスを定義。2行目にて参照。 -->
<bean id="adviceClass" class="jp.example.aop.advice.AdviceImpl">
  ...
</bean>

Pointcut

bean定義例
<aop:config>
  <aop:pointcut id="pointcut1" expression="execution(* com.xyz.myapp.service.*.*(..))"/>
  <aop:pointcut id="pointcut2" expression="com.xyz.myapp.SystemArchitecture.businessService()"/>
</aop:config>

expressionにはPointcutのパターン(Primitiveポイントカット)を記述する。

Primitiveポイントカット
  • execution
    • 呼出先の「メソッド」、「コンストラクタ」を指定。
    • フォーマット)execution([型] [メソッド])
    • 【例】execution(String execute())
    • ワイルドカード「*」「..」「+」を使用可能
      • 「*」
        • 任意の型との一致、またはクラス名、パッケージ名の一部の代わりとして使用可能。
        • 【型をワイルドカードにした例】execution(* execute())
        • 【クラス名の一部をワイルドカードにした例】execution(String com.xyz.myapp.System*.exec())
      • 「..」
        • 任意の引数との一致、任意のパッケージ、クラス名の一部の代わりとして使用可能。
        • 【引数をワイルドカードにした例】execution(* execute(..))
        • 【パッケージの一部をワイルドカードにした例】execution(String com.xyz..exec())
      • 「+」
        • クラス名もしくはインターフェース名の右側に書くことで、該当するクラスのサブクラスやインターフェースの実装がすべて指定される。
        • 【例】execution(* com.xyz.myapp.SystemArchitecture+.*(..))
  • within
    • 呼び出し元の「クラス」を指定。指定されたクラスから呼び出される。
  • this
    • 呼び出し元の「クラス」を指定。指定されたクラスから呼び出される。
    • withinとは、親クラスで定義されたメソッドの呼び出しも対象となる点が異なる。
  • target
    • 呼び出し先のクラスを指定。ただし、staticフィールドは対象外。
  • args
    • 呼び出し先の「メソッド」の引数の型を指定。

Primitiveポイントカットは論理演算子「and」「or」「not」を使用することができる。
【例】expression="execution(* com.xyz.myapp.service.*.*(..)) or execution(* com.abc.myapp.service.*.*(..))"

Advice

種類
  • Before Advice
    • Joinpointの前に実行するAdvice。
  • After Advice
    • Joinpointの後に実行するAdvice。
  • Around Advice
    • Joinpointの前後で実行されるAdvice。
    • Before AdviceプラスAfter Advice
  • After Returning Advice
    • Joinpointが完全に正常終了した後に実行されるAdvice。
    • Advice対象メソッドの戻り値を受けて処理を行う。
  • After Throwing Advice
    • Joinpointで例外が発生した場合に実行されるAdvice。
    • Advice対象メソッドスローした例外を受けて処理を行う。
bean定義例
<aop:config>
  <aop:aspect id="aspect" ref="adviceClass">
  
    <aop:pointcut id="pointcut1" expression="execution(* getMessage())" />
    
    <!-- 【Before Advice】pointcut-refにて上記Pointcut定義の参照、methodにて"adviceClass"で定義したクラスのどのメソッドの処理を挟み込むか指定 -->
    <aop:before pointcut-ref="pointcut1" method="beforeMethod" />
    
    <!-- 【After Advice】Pointcutをpointcut-refで参照せずに直定義することもできる -->
    <aop:after pointcut=" execution(* jp.co.xyz.app1.service.*.*(..))" method="afterMethod" />
    
    <!-- 【Around Advice】 -->
    <aop:around pointcut-ref="pointcut1" method="aroundMethod" />
    
    <!-- 【After Returning Advice】returningにはAdvice対象メソッドの戻り値を代入するAdvice実装メソッドの引数名を指定 -->
    <aop:after-returning pointcut-ref="pointcut1" method="afterReturningMethod" returning="returnedValue" />
    
    <!-- 【After Throwing Advice】throwingにはAdvice対象メソッドスローする例外を代入するAdvice実装メソッドの引数名を指定 -->
    <aop:after-throwing pointcut-ref="pointcut1" method="afterThrowingMethod" throwing="thrownException" />
    
  </aop:aspect>
</aop:config>

<!-- Adviceを実装したクラスのbean定義 2行目にて使用 -->
<bean id="adviceClass" class="jp.example.aop.advice.AdviceImpl" />
Advice実装例(jp.example.aop.advice.AdviceImpl)

を使用する場合、Adviceの実装はPOJOとなる。

package jp.example.aop.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class AdviceImpl {

  public void afterMethod() {
    System.out.println("afterMethod called.");
  }

  public void beforeMethod() {
    System.out.println("beforeMethod called.");
  }

  public void aroundMethod(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("aroundMethod called.");
    
    System.out.println("before proceed.");
    
    // Advice対象のメソッドが実行される。
    pjp.proceed();
    
    System.out.println("after proceed.");
  }

  /**
   * @param returnedValue Advice対象メソッドの返り値。
   *                      String型になっているが、Advice対象メソッドの返り値の型に合わせる。
   */
  public void afterReturningMethod(String returnedValue) {
    System.out.println("afterReturningMethod called.");
    System.out.println("returnedValue is" + returnedValue + ".");
  }

  /**
   * @param thrownException Advice対象メソッドがスローする例外。
   */
  public void afterThrowingMethod(Exception thrownException) {
    System.out.println("afterThrowingMethod called.");
    System.out.println("thrownException is" + thrownException + ".");
  }
}

アノテーションを利用したAOP

  • @Aspect
    • Adviceとなるクラスを指定するアノテーション
  • @Before
    • Before Adviceとなるメソッドを指定するアノテーション
  • @After
    • After Adviceとなるメソッドを指定するアノテーション
  • @AfterReturning
    • After Returning Adviceとなるメソッドを指定するアノテーション
  • @Around
    • Around Adviceとなるメソッドを指定するアノテーション
  • @AfterThrowing
    • After Throwing Adviceとなるメソッドを指定するアノテーション

★これらのアノテーションを使用する場合はbean定義ファイルに「」の定義が必要

利用例
@Aspect
public class AspectMessage {
	
  @After("execution(* exMethod())")
  public void hoge() {
    // メソッド終了後に動くAdvice
    System.out.println("after called.");
  }
}

Advisor

bean定義例
<aop:config>
  <aop:advisor pointcut="* exec(..)" advice-ref="adviceImpl" />
</aop:config>

<bean id="adviceImpl" class="jp.example.aop.advice.impl.AdviceImpl" />

を使用する場合はAdviceの実装はPOJOであったが、
を使用する場合は下記のようにインターフェースを実装する必要がある。
また、トランザクション管理などSpringの既存機能を利用する場合はではなくを使用することになる。

  • Around Advice
    • org.aopalliance.intercept.MethodInterceptor
  • Before Advice
    • org.springframework.aop.MethodBeforeAdvice
  • After Returning Advice
    • org.springframework.aop.ThrowsAdvice
  • After Throwing Advice
    • org.springframework.aop.AfterReturningAdvice

参考
http://www.asahi-net.or.jp/~gq4r-msm/html/essay09/html/toUseSpringAop.html
http://static.springsource.org/spring/docs/2.5.x/reference/index.html