1.はじめに

AOPというのは、プログラムの方法論です。Springはその方法を実現する機能を提供しています。この解説は、AOPそのものの解説ではなくて、SpringのAOPの使い方の解説です。AOPそのものに興味のある方は別の資料を参照下さい。

Springの資料によれば、Springが提供している機能は一般のAOPすべてを実装したものではありません。AOPを最も広範に実装している著名なソフトウェアはいくつかあり、その一つがAspectJです。

もし、SpringのAOP機能の範囲外のAOPの機能を使う場合は、AspectJと組み合わせてSpringを使えます。SpringのAOPを使わないでAspectJだけを使う選択肢もあります。

SpringのAOPはSpringの機能を実装する技術として、Spring自身でも使われています。たとえば、トランザクションの機能にSpringのAOPが使われています。また、SpringのAOPはSpringとなじむように実装されていますので、実装されている範囲に限ればSpringの中ではSpringのAOPが使いやすいといえます。

 

2.AOPとは

2.1 概説

Springの実装もしくは使い方に沿った意味でAOPを概説します。「はじめに」に書きましたが、これはAOPの解説ではありませんので一般性を欠いていることをはじめにおことわりしておきます。

業務ロジックのコードの中に現れるログやトランザクションの処理は、本来の業務ロジックを分かりにくくする問題があります。これらを業務ロジックのクラスの外に出すプログラムの手法がAOPです。

AOPを使うと業務ロジックのコードの中にログやトランザクションの処理を書かなくてすませることができます。コードの中に書かなくても、期待する処理がされるということは実行時に何らかの方法でそこに制御を移す仕組みが働いているはずです。このための具体的な方法はAOPを実装するソフトウェアによって異なります。

AOPでは独自の用語を使われています。それについて簡単に解説します。

プログラミング言語によって多少言い方が違いますが、プログラムとかクラス、関数とかメソッド、サブルーチンなどで処理する「問題」をコンサーン(concern)といいます。この解説では「関心事」(*1)と呼びます。元の用語から分かるように、「問題」とか「懸念」とかで言い換えていいものでしょう。

注*1:この解説ではAOPで使われる独特の用語は、「関心事(concern)」の形で訳語と元の言葉を一緒に表記します。AOP関係の訳語はまだ固まっていないようですので、この解説で使っているものが正しい訳とは限りませんのでこの形を使います。

 

例えば関心事(concern)には次のようなもの(こと)があります。

(1)円からドルに換算する。

(2)ある商品の在庫量を調べる。

(3)実行時刻を印刷する。

(4)処理に失敗するとrollbackする。

 

複数の関心事(concern)が一つのプログラムとかクラス、メソッド、サブルーチンなどで処理されます。プログラム言語の一つの役割は、一つのクラス、メソッド、サブルーチンでは一つの関心事(concern)だけを対象にすることです。たとえば、「円からドルに変換する」メソッドと「ある商品の在庫量を調べる」メソッドを恐らく同じクラスには含めないでしょう。

ところが、関心事(concern)の中には、別のクラスやメソッドに分離できないものがあります。「実行時刻を印刷する」とか「処理に失敗すると元に戻す」処理は「円からドルに変換する」メソッドや「ある商品の在庫量を調べる」の中に混じって使われる処理です。こういう処理に対応する関心事(concern)を交錯関心事(cross-cutting concern)と言います。縦横に交わる関心事(concern)という意味でしょう。

ログやトランザクションは交錯関心事(cross-cutting concern)の例です。アクセスコントロールも交錯関心事(cross-cutting concern)の一つです。

先に書きましたように、「一つのクラス、メソッド、サブルーチンでは一つの関心事(concern)だけを対象にする」がプログラム言語の役割の一つです。しかし、交錯関心事(cross-cutting concern)については、従来のプログラム言語はこの役割を果たせないのです。それをできるようにする方法論がAOPです。

AOPは交錯関心事(cross-cutting concern)とそうでない関心事(concern)を分離して実装する方法論です。こういう分け方をしたときに交錯関心事(cross-cutting concern)でない関心事(concern)のことを基礎関心事(base consern)と呼ぶことにします。図2.1-1はこの様子を図示したものです。

 

図2.1-1 関心事(concern)の分離(p3)

図2.1-1 関心事(concern)の分離

 

分離前のモジュールは二つのモジュールに分割されます。実際の分割はもっと複雑かもしれません。論理的には基礎関心事(base concern)と交錯関心事(cross-cutting concern)に分かれるのです。

分離した後、AOPの仕掛けを使って分離前と同じ処理ができるようにするのです。

なお、この解説では、分離後の基礎関心事(base concern)に対応する処理と交錯関心事(cross-cutting concern)に対応する処理のことを、それぞれ基礎関心事処理と交錯関心事処理と呼ぶことにします。後に述べるAOPの実装法から基礎関心事処理のことはターゲットとも言われます。この解説ではターゲットいう言葉も使います。

 

注1 モジュールの意味はAOPの実装に依存します。オブジェクト指向言語の場合はクラスもしくはクラス群を想定すればいいと思います。

注2 AOPそのものは方法論なので仕掛けまで言及していません。「AOPの仕掛け」とは「AOPを支援するソフトウェアの仕掛け」と解釈して下さい。どんな仕掛けかはそのソフトウェアに依存します。

 

図2.1-1をシーケンス風に書くと図2.1-2になります。

図2.1-2 関心事(concern)の分割(シーケンス図)(p3)

図2.1-2 関心事(concern)の分割(シーケンス図)

 

灰色の箱は交錯関心事(cross-cutting concern)を指します。分離後は、交錯関心事(cross-cutting concern)が基礎関心事(base concern)と分離され、基礎関心事(base-concern)から呼び出される形になります。

 

例を示します。

リスト2.1-1はAOPを使わないで記述したトランザクションを含むDBへのレコード追加の処理です。

これを分割してできた基礎関心事処理と交錯関心事処理がそれぞれリスト2.1-2とリスト2.1-3です。

 

リスト2.1-1 分割前

...
public class Service{
...
    public registerNewMember(Member member){
        Transaction tx = TransactionFactory.getInstance(...);
        tx.beginTransaction();
        try{
            dao.addRecors(tx, ...);
            tx.commit();
        }catch (Exception e){
            tx.rollback();
            log.error(...);
        }
        tx.endTransaction();
    }
...
}

赤字の部分が交錯関心事(cross-cutting concern)のための処理

 

リスト2.1-2 基礎関心事処理

...
public class Service{
    public registerNewMember(Member member){
            dao.addRecors(tx, ...);
    }

 

リスト2.1-3 交錯関心事処理

public class ServiceAspect{
    public void before(){
        Transaction tx = TransactionFactory.getInstance(...);
        tx.beginTransaction();
    }
    public after(){
        tx.commit();
    }
    public void afterThrowing throws Throwable(Exception e){
        tx.rollback();
        log.error(...);
        throw e;
    }
    public void after(){
        tx.endTransaction();
    }
}

 

AOPを大まかに説明しましたので、これをもとに、AOPで使われる他の主な用語を列挙して説明します。

 

(1)結合点(ジョインポイント、join points,joinpoint)

基礎関心事処理から交錯関心事処理に制御を移せる点の全体を結合点(join points)と呼びます。

プログラムの実行の動きを1ステップずつ進む流れとして捉えると、このステップの前後が結合点(join points)と考えることが出来ます。

結合点群(join points)として何が使えるかはAOPを支援するソフトウェアに依存します。代表的な例は、「メソッドの呼び出し直後」、「メソッドから戻る直前」等です。

 

(2)切込み点(ポイントカット、pointcut)

結合点群(join points)は、「制御を移せる点(群)」です。結合点群(join points)のうち、実際に制御を移す結合点群(join points)を特定する条件のことを切込み点(pointcut)と言います。

切込み点(pointcut)を指定する具体的な方法や細かさはAOPを支援するソフトウェアに依存します。代表的な例はメソッド名とそのメソッド呼び出し直後又はメソッドから戻る直前で特定する方法です。

 

(3)様相(アスペクト、aspect)

交錯関心事処理をまとめたモジュールを意味します。オブジェクト指向で実装した場合、様相(aspect)はクラス(群)で実装されるのが普通です。リスト2.1-3はその例です。

 

(4)勧告(アドバイス、advice)

様相(aspect)の中の一単位の処理です。オブジェクト指向で実装した場合、メソッドが対応します。ある結合点(join point)から呼び出されます。リスト2.1-3の各メソッドが勧告(advice)に相当します。

 

(5)編込み(weaving)

基礎関心事処理と交錯関心事処理を組み合わせて、分離前と同じ処理をするようにすることです。

実際には、「分離前」のコードを作ってから分離するわけではないので、基礎関心事処理と交錯関心事処理を組み合わせて「目的の処理」をするようにすることといえます。

リスト2.1-2とリスト2.1-3を編込んでできる処理をリスト2.1-4に示します。

 

リスト 2.1-4 編込み後の処理

        try{
         <<結合点(join point) 1>> 呼び出し ServiceAspect:before
            dao.addRecors(tx, ...);
         <<結合点(join point) 2>> 呼び出し ServiceAspect:after
        }catch (Exception e){
         <<結合点(join point) 3>> 呼び出し ServiceAspect: afterThrowing
            throw e;
        }

 

 

 

3.Spring1.2のAOP機能

3.1 Spring1.2のAOP機能

ここまではやや一般的にAOPについて説明しました。ここからはSpringのAOPに絞り込んでいきます。

SpringのAOPの機能はSpring2.0と1.2では違っています。Spring2.0AOPはSpring1.2AOPより拡張されています。Spring2.0でも1.2の機能が使えます。この章ではSpring1.2の仕様について説明します。Spring2.0AOPについては別の章で説明します。

Spring1.2の仕様はAOPを実現するための「仕掛け」が一部見えます。このおかげでAOPを理解しやすいと思います。実際に使うのはSpring2.0AOPの方が多くなると思いますが、AOPを理解する観点では、Spring1.2AOPから学習するのは無駄ではありません。

Spring1.2AOPは低位のAPIを提供しています。これを使うと独自の様相(aspect)のクラスを作成できるでしょう。しかし、ここでは、Spring1.2AOPの低位のAPIを使わない範囲の機能にとどめます。

 

Springで使える結合点群(join points)は次の5種です。このうち、Spring1.2で使えるのは最初の4種です。5番目はSpring2.0以降で使えます。

 

表3.1-1 サポートされている結合点(join points)の型

# 説 明
before 呼び出したメソッドの処理開始直前
after returnig メソッドから戻る直前(呼び出されたメソッドが例外を投げないでリターンした直後)
around 上記の双方
after throwing 呼び出し先で例外が生じて呼び出し元にもどる時点
after メソッドから戻る直前(呼び出されたメソッドが例外を投げた場合も投げない場合も該当する)

 

これらはSpringのアプリケーションコンテキストで定義されたbeanのメソッド実行時に適用されます(任意のクラスではありません)。

型は結合点(join points)の型です。ただし、実際の実装では勧告(advice)は結合点(join points)の型に対応して違った型(*1)になるので、これは勧告(advice)の型ともいえます。この解説では、結合点(join points)の型と勧告(advice)の両方で型を使います。

注*1:具体的な実装では勧告(advice)を担うメソッドの名前、正確にはシグネチャ

 

Spring1.2AOP機能は、この他にメソッドを特定のクラスに埋め込むことが出来ます。この機能は導入(introduction)と呼ばれています。また、混入(mix-in,mixin)とも呼ばれます。導入(intoroduction)は結合点群(join points)と切込み点(pointcut)では規定できないので上記の結合点群(join points)と関係した機能とは別に説明します。

この節は、先にあげた4種の結合点群(join points)について説明します。

 

Spring1.2のAOPの実装法を図3.1-1に示します。

 

図3.1-1 SpringのAOPの実装(p8)

図3.1-1 SpringのAOPの実装

 

基礎関心事処理のproxyを設けます。proxyは基礎関心事処理の他に基礎関心事処理が実装しているinterfaceとAdvisorと関連を持っています。さらに、AdvisorはAdviceクラス(交錯関心事処理の一部)と関連を持ち、切込み点(pointcut)をそのプロパティとして保持しています。

AdvisorはSpring1.2AOPの実装独自の考え方で、一般の様相(aspect)に相当します。勧告(advice)を一つしか持てない様相(aspect)です(注*1)。Spring1.2には様相(aspect)としてAdvisorしかありません。

注 *1: 正確には、様相(aspect)を担う一つのクラスに切込み点(join points)の型が異なる勧告(advice)を一つずつ含めることは出来ます。Spring2.0ではこの制約はなくなります。

 

基礎関心事処理クラスへの要求(呼び出し)を、いったんproxyが中断します。その呼び出しが切込み点(pointcut)に合えばそのAdvisorに関連付けられた勧告(advice)を呼び出します。勧告(advice)から戻ってくるとproxyが中断した元の要求(呼び出し)の処理をします。proxyが中断した元の要求(呼び出し)が切込み点(pointcut)に合致しない場合は、中断した要求(呼び出し)の処理をします。

基礎関心事処理クラスへの要求(呼び出し)が、呼び出し先から戻ってくる場合も、その戻りを中断します。呼び出しのときと同じように、切込み点(pointcut)に照らして、勧告(advice)を呼び出した後、又は何もしないで基礎関心事処理クラスへの要求(呼び出し)元に戻ります。

基礎関心事処理クラスの中で例外が発生した場合も、戻りの場合と同じように処理されます。

 

勧告(advice)はSpringが提供するinterfaceを実装します。そのメソッド名は結合点群(join points)の型で決まっています。

ここで重要なことは、基礎関心事処理クラスの利用クラスは、proxyの存在を考えないで基礎関心事処理クラスを使うコードを書くことができるということです。実行時にproxyを通して基礎関心事処理クラスに制御が移る設定はSpringのアプリケーションコンテキストで設定されるだけです。これがAOPの目的です。

 

3.2 Spring1.2 AOPの簡単な例

これまでに説明に使ってきた例を実装して、さらに具体的にSpring1.2 AOPの機能を説明します。対応するソースはパッケージmsh.pub.springaop.descです。例題1も併せて参照下さい。

図3.1-1の構成をリスト3.1-1とリスト3.1-2、リスト3.1-3で使った例に当てはめると図3.2-1になります。

 

図3.2-1 例題のクラス図(p9)

図3.2-1 例題のクラス図

 

図3.1-1と図3.2-1の対応は表3.2-1の通りです。

 

表3.2-1 Spring1.2 AOP 実装と例の対応

Spring1.2 AOP 実装
基礎関心事処理クラス Service
基礎関心事処理interface IService
Advisor RegexpMethodPointcutAdvisor(3インスタンス)(*1)
Adviceクラス AdviceBefore, AdviceAfter, AdviceThrowing
基礎関心事処理クラスを利用するクラス Client

注 *1:図3.2-1の中ではクラス名をAdvisorと記しています。

 

この構成をSpringのアプリケーションコンテキストの中に記述します。この設定を少しずつ見ていきます。

 

3.2.1 基礎関心事処理クラス

 

リスト3.2-1基礎関心事処理クラス

  <bean id="theService" class="msh.pub.springaop.desc.impl.Service"/>

 

AOPを使わない場合と変わらない設定です。

 

3.2.2 勧告(advice)

 

リスト3.2-2 勧告(advice)クラス

  <bean id="adviceBefore" class="msh.pub.springaop.desc.impl.AdviceBefore"/>
  <bean id="adviceAfter" class="msh.pub.springaop.desc.impl.AdviceAfter"/>
  <bean id="adviceThrowing" class="msh.pub.springaop.desc.impl.AdviceThrowing"/>

 

AdviceはAOPに関係していますが、その設定は通常のクラスと変わりません。

3個の勧告の例ですが数に制限はありません。

 

3.2.3 Advisor

 

リスト3.2-3 勧告(advice)クラス

  <bean id="advisorBefore"
      class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
      <property name="advice">
          <ref local="adviceBefore"/>
      </property>
      <property name="patterns">
          <list>
              <value>.*</value>
          </list>
      </property>
  </bean>

 

AdvisorのクラスはSpringが提供するクラスです。このクラスに二つのプロパティを設定しています。図3.2-1に示すように勧告(advice)と切込み点(pointcut)を設定するプロパティです。

プロパティadviceにはadviceBeforeを指定しています。

プロパティpatternsには切込み点(pointcut)として使う正規表現を指定します。複数組の正規表現を設定できます。ここでは、「.*」を指定していますので、すべてのメソッドが対象になります(正規表現 .*はすべての文字列に合致します)。

上記の例はadviceBeforeに対応するAdvisorです。他の二つの勧告(advice)についても同様にAdvisorを設定できます。説明は略します。

 

3.2.4 proxy

 

リスト3.2-4 proxyクラス

  <bean id="proxy"
      class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="proxyInterfaces">
            <value>msh.pub.springaop.desc.IService</value>
      </property>
      <property name="target"><ref local="theService"/></property>
      <property name="interceptorNames">
          <list>
              <value>advisorBefore</value>
              <value>advisorAfter</value>
              <value>advisorThrowing</value>
          </list>
      </property>
  </bean>

 

proxyのクラスはSpringが提供するクラスです。このクラスに次の3個のプロパティを設定します。

(1) 基礎関心事処理クラスが実装しているinterface

(2) 基礎関心事処理クラス(のインスタンス)

(3) Advisor

 

Advisorは複数個指定できます。

 

3.2.5 基礎関心事処理を使うクラス

 

リスト3.2-5 基礎関心事処理を使うクラス

  <bean id="theClient" class="msh.pub.springaop.desc.impl.Client">
      <property name="service" ref="proxy" />
  </bean>

 

基礎関心事処理を使うクラスはAOPを使わない通常のクラスですから、その指定も通常通りです。ただ、Serviceの参照を設定するプロパティに設定するのはServiceの参照ではなくproxyです。

 

3.3 導入(introduction)機能

これまで説明したAOPは、特定の結合点(join points)で処理を横取りして、勧告(advice)に制御を渡す手法を使っています。

AOPにはこれとは別に導入(introduction)の手法があります。特定のクラスにフィールドやメソッドを埋め込みます。ただし、Springではメソッドを埋め込むだけです。

図3.3-1は導入(introduction)の概要を説明する図です。

 

図3.3-1 導入(introduction)(p13)

図3.3-1 導入(introduction)

 

基礎関心事処理クラスBaseにメソッドmethod1を持つAdviceクラスを導入(introduction)した場合です。導入すると次の二つの効果があります。

(1)導入(introduction)後は、BaseクラスはIAdviceインタフェースを実装したのと同じように見えます。実装の内容はAdviceクラスで実装した内容です。

(2)Baseクラスのメソッドの呼び出しをインターセプトして、そのメソッドの実際の呼び出しの前後で処理を追加できます。

 

Spring1.2の導入(introduction)を実装する仕掛けは結合点群(join points)に関する機能の実装とほぼ同じです。図3.3-2に導入(introduction)の実装を示します。

 

図3.3-2 Spring1.2の導入(introduction)の実装(p14)

図3.3-2 Spring1.2の導入(introduction)の実装

 

結合点群に関する実装との違いは次の4点です。

(1)pointcutは指定しない。(*1)

(2)AdviceクラスはSpringが提供する親クラスを継承する。

   結合点群に関する機能では、Springが提供するinterfaceを実装します。

(3)導入するinterfaceをAdviceクラスにを実装する。

(4)導入するinterfaceをAdvisorクラスに登録する。

(5)Adviceと導入するinterfaceはAdvisorクラスのコンストラクタの引数として与えます。

   結合点群に関する機能では、AdviceはAdvisorクラスのプロパティとして与えます。

注 *1:クラスはproxyのプロパティとして指定されます。pointcutはそのクラスだという解釈も出来ます。

 

3.4 導入(introduction)の例

ソース中のパッケージ名:msh.pub.springaop.intro

導入を使ってsetterの機能を有効にしたり無効にしたりする例題を紹介します。

これは、資料1に基づく例題です。クラスの構成を図3.4-1に示します。

 

図3.4-1 導入(introduction)の例題のクラス図(p15)

図3.4-1 導入(introduction)の例題のクラス図

 

これは、図3.3-2に具体的なクラスを割り当てた構成になっています。Clientから見えるServiceのメソッドは次の5メソッドです。

(1)setName

(2)getName

(3)lock

(4)unlock

(5)locked

 

このうち上の2メソッドはServiceクラスで実際に実装されています。下の3メソッドはLockMixinAdviceクラスの中に実装されたメソッドです。これらが、Clientの中ではあたかもServiceに実装されているかのように見えます。

lockメソッドを呼び出すとlockedフィールドがtrueに設定されます。unlockメソッドを呼び出すとlockedフィールドがfalseに設定されます。lockedメソッドははフィールドlockedの値を取り出します。

LockMixinAdviceクラスのinvokeはServiceの呼び出しをインタセプトして呼び出されるメソッドです。

ClientはsetNameを使ってServiceにnameを設定しようとします。ただし、フィールドlockedがfalseのときは値を設定できますが、trueのときは設定できません。つまりimmutable(変更不可の)クラスになっています。

 

3.4.1 導入(introduction)されたメソッドの呼び出し

例としてlockメソッドを取り上げます。Clientの中でのlockメソッドの呼び出し方をリスト3.4-1に示します。

 

リスト3.4-1導入(introduction)されたメソッドの呼び出し

    ILockable lockMixin = (ILockable)service;
    ...
    lockMixin.lock();

 

serviceを導入するinterfaceでキャストして使います。serviceは見かけはターゲットのクラスです。実際にはproxyです。

 

3.4.2 基礎関心事処理クラスのメソッド呼び出しのインターセプト

Serviceクラスのメッソドの呼び出しをインターセプトして呼び出されるinvokeメソッドの中で、lockedがtrueのときはsetXxxxメソッドの実際の処理をスキップするコードをリスト3.4-2に示します。

 

リスト3.4-2基礎関心事処理クラスのメソッド呼び出しのインターセプト

public Object invoke(MethodInvocation invocation) throws Throwable {
    if (locked() && invocation.getMethod().getName().indexOf("set") == 0)
        throw new LockedException("Exception " + invocation.getMethod().getName() + " is locked.");
    return super.invoke(invocation);
}

 

アプリケーションコンテキストの設定方法を見ていきます。

 

3.4.3 基礎関心事処理クラス

 

リスト3.4-3基礎関心事処理クラス

  <bean id="theService" class="msh.pub.springaop.desc.impl.Service"/>

 

AOPを使わない場合と変わらない設定です。

 

3.4.4 勧告(advice)

 

リスト3.4-4 勧告(advice)クラス

  <bean id="lockMixinAdvice" class="msh.pub.springaop.intro.impl.LockMixinAdvice"/>

 

3.4.5 Advisor

 

リスト3.4-5 Advisor

  <bean id="advisorLockMixin" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
    <constructor-arg ref="lockMixinAdvice"/>
    <constructor-arg type="java.lang.Class" value="msh.pub.springaop.intro.ILockable"/>
  </bean>

 

設定の方法は通常のクラスと同じです。ただし、引数付きのコンストラクタを使っています。第2引数は、Spring beanのidではなく、タイプがClassの定数を設定しています。これらの機能はAOP専用のものでなくSpringを使うときにどこでも使える機能です(資料3)。

 

3.4.6 proxy

 

リスト3.4-6 proxyクラス

  <bean id="proxy"
      class="org.springframework.aop.framework.ProxyFactoryBean">
      <property name="proxyInterfaces">
          <value>msh.pub.springaop.intro.IService</value>
      </property>
      <property name="target"><ref local="theService"/></property>
      <property name="interceptorNames">
          <list>
              <value>advisorLockMixin</value>
          </list>
      </property>
  </bean>

 

proxyのクラスはSpringが提供するクラスです。このクラスに次の3個のプロパティを設定します。

(1) 基礎関心事処理クラスが実装しているinterface

(2) 基礎関心事処理クラス(のインスタンス)

(3) Advisor

 

3.4.7 基礎関心事処理を使うクラス

 

リスト3.4-7 基礎機能を使うクラス

  <bean id="theClient" class="msh.pub.springaop.desc.impl.Client">
      <property name="service" ref="proxy" />
  </bean>

 

基礎関心事処理を使うクラスはAOPを使わない通常のクラスですから、その指定も通常通りです。ただ、Serviceの参照を設定するプロパティに設定するのはServiceの参照ではなくproxyの参照です。

 

3.5 まとめ

 

AOPを使う利点はリスト2.1-2に凝縮されています。まとめると次の2点になります。

(1)業務ロジックが読みやすくなる。

(2)再利用性が高くなる。

 

 

 

4. Spring2.0AOP

この章ではSpring2.0で強化されたAOPの機能を紹介します。

Spring1.2は、アプリケーションコンテキストの中でproxyを明示的に設定する方式でした。Spring2.0では、proxyを明示的に設定する必要がありません。具体的な指定法は次の二つです。

(1) スキーマに基づくAOPサポート(Schema-based AOP support)

    aop 名前空間タグを使う方法

    <aop:ttt ...>を使って、pointcut、advice、aspectを指定します。

    この解説ではこれをxsd-AOPと略記します。

(2) AspectJアノテーションに基づくAOPサポート

    Java5でサポートされたアノテーションの機能を使って、AOPに必要な情報をソースコードに書き入れます。この記述の仕様はAspectJに準じています。

    この解説ではこれを@Aspect-AOPと略記します。

 

また、必要な場合は、Spring1.2のAOPはdtd-AOPと略記します。

 

4.1 スキーマに基づくAOPサポート(xsd-AOP)

4.1.1様相(aspect)

 

リスト4.1-1 様相(aspect)の定義

<aop:config>
  <aop:aspect id="aspect1" ref="aspectBean1">
    ...
  </aop:aspect>
</aop:config>

<bean id=" aspectBean1" class="...">
  ...
</bean>

 

AOPに関する設定はaop:config要素の中で行います。

様相(aspect)は、この中で、aop:aspect要素で行います。idと様相(aspect)を実装したbeanのidをここで指定します。様相(aspect)を実装したbeanは通常のbeanと同じ方法で指定します。

 

4.1.2 切込み点(pointcut)

 

リスト4.1-2 切込み点(pointcut)の定義

<aop:config>
  ...
  <aop:pointcut id="pointcut1"
        expression="execution(* jp.co.xyz.app1.service.*.*(..))"/>
  ...
</aop:config>

 

切込み点(pointcut)は、aop:config中で、aop:aspect要素で行います。指定できる情報はidとexpressionです。詳細は資料2を参照下さい。

切込み点(pointcut)を指定する場合は、aop:aspectより前に指定しなければなりません。

 

4.1.3 勧告(advice)

(1)before

 

リスト4.1-3 before勧告(advice)の定義

<aop:aspect id="aspect1" ref="aspectBean1">
  ...
  ①<aop:before
      pointcut-ref="pointcutBefore"
      method="doSomethingBefore"/>
  ②<aop:before
      pointcut=" execution(* jp.co.xyz.app1.service.*.*(..))"
      method="doSomethingBefore"/>
  ...
</aop:aspect>

 

勧告(advice)は様相(aspect)の定義の中で設定します。一つの様相(aspect)に複数の勧告(advice)を指定できます。これらの勧告(advice)は同じ型の勧告(advice)である必要はありません。

使う切込み点(pointcut)はaop:cofigのレベルで定義された切込み点(pointcut)をidで参照する方法と直接定義を書く方法があります。①は参照する方法、②は直接定義する方法を使っています。

切込み点(pointcut)を指定するこの二つの方法は他の型の勧告についても同じです。この節の残りの例には参照する方法だけ使います。

 

(2)after returning

 

リスト4.1-4 after returning勧告(advice)の定義

<aop:aspect id="aspect1" ref="aspectBean1">
  ...
  ①<aop:after-returning
      pointcut-ref="pointcutAfterReturning"
      method="doSomethingAfterReturning1"/>
  ②<aop:after-returning
      pointcut-ref="pointcutAfterReturning"
      returning ="rtn"
      method="doSomethingAfterReturning2"/>
  ...
</aop:aspect>

 

指定の方法はbefore型と同じです。ただし、戻り値を取り出す設定ができます。①は戻り値を取り出す設定はしていません。②は戻り値を取り出す設定(returning属性)をしています。引数の名前を指定します。この場合、doSomethingAfterReturning2の引数は次の形になります。

 

リスト4.1-5 戻り値を受け取るためのafter returning勧告(advice)メソッドのシグネチャ

public void doSomethingAfterReturning2 (Object ret) {
  ...
}

 

(3)around

 

リスト4.1-6 around勧告(advice)の定義

<aop:aspect id="aspect1" ref="aspectBean1">
  ...
  <aop:around
      pointcut-ref="pointcutAround"
      method="doSomethingAround"/>
  ...
</aop:aspect>

 

指定の方法はbefore勧告(advice)と同じです。aroudはbeforeとafter-returningを兼ねています。before又はafter-returningで済む場合は、aroundを使わないでそちらを使うべきです。

around勧告(advice)のメソッドは次の形です。第1引数は必ずProceedingJoinPointです

 

リスト4.1-7 before勧告(advice)メソッドのシグネチャとターゲットのメソッドの呼び出し

public Object doSomethingAround(ProceedingJoinPoint pjp) throws Throwable {

    // something todo before invoking

    Object ret = pjp.proceed();

    // something to do after returning

    return ret;
}

 

(4)after throwing

 

リスト4.1-8 after throwing勧告(advice)の定義

<aop:aspect id="aspect1" ref="aspectBean1">
  ...
  ①<aop:after-throwing
      pointcut-ref="pointcutThrowing"
      method="doSomethingThrowing1"/>
  ②<aop:after-throwing
      pointcut-ref="pointcutThrowing"
      throwing="myException"
      method="doSomethingThrowing2"/>
  ...
</aop:aspect>

 

指定の方法はbefore型と同じです。ただし、投げられた例外オブジェクトを勧告の引数として取り出す設定ができます。①は例外オブジェクトを受け取る設定はしていません。②は例外オブジェクトを受け取る設定(throwing属性)をしています。引数の名前を指定します。この場合、doSomethingThrowing2の引数は次の形になります。

 

リスト4.1-8 after throwing勧告(advice)メソッドのシグネチャ

public void doSomethingThrowing2(MyException myException) {
  ...
}

 

(5)after(finally)

 

リスト4.1-8 after勧告(advice)の定義

<aop:aspect id="aspect1" ref="aspectBean1">
  ...
  <aop:after
      pointcut-ref="pointcutAfter"
      method="doSomethingAfter"/>
  ...
</aop:aspect>

 

after勧告(advice)は正常に戻る場合でも例外で戻る場合でも実行される勧告(advice)です。after-returning勧告(advice)は正常に戻る場合だけしか実行されません。

 

例題2はxsd-AOPを使った例です。

 

4.2 @Aspectサポート

@Aspectは、Java5でサポートされたアノテーションの機能を使って通常のJavaのクラスにJava5でサポートされたアノテーションを付けて、AOPに必要な情報を設定する方式です。Spring2.0は、AspectJが提供するライブラリを使って、AspectJと同じ仕様のアノテーションを解釈します。ただし、実行時のライブラリはSpringAOPのものを使っています。AspectJのコンパイラーと編込みソフトウェアに依存しません。

AspectJの全機能を使うこともできますが、ここでは触れません。

 

@Aspectを有効にするための設定

@AspectのAOPサポートを有効にするにはアプリケーションコンテキストに次の文を加えなければなりません。

(1)スキーマ(xsd)を使った指定

 

リスト4.2-1 @Aspectを有効にする設定(xsdを使った場合)

<aop:aspectj-autoproxy/>

 

(2)dtdを使った指定

 

リスト4.2-2 @Aspectを有効にする設定(dtdを使った場合)

<bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" />

 

4.2.1様相(aspect)

 

リスト4.2-3 様相(aspect)のbeanの定義

<bean id="aspect1" class="jp.co.xyz.app1.Aspect1">
   <!-- configure properties of aspect here as normal -->
</bean>

 

通常のクラスと同じ方法で様相(aspect)を実装するクラスをbeanとして設定します。

様相(aspect)を実装するクラスは次の形で作成します。

 

リスト4.2-4 @Aspectアノテーション

package jp.co.xyz.app1;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class Aspect1{
  ...
}

 

class文のアノテーションとして@Aspectを指定します。

通常のクラスと同じようにフィールドとメソッドを記述できます。また、この後で説明する切込み点(pointcut)や勧告(advice)、導入(introduction)をアノテーションを付けて記述できます。

 

4.2.2 切込み点(pointcut)

 

リスト4.2-5 切込み点(pointcut)アノテーション

@Pointcut("execution(* someMethod(..))") // the pointcut expression
private void pointcut1() {} // the pointcut signature

 

切込み点(pointcut)の定義は、対象のmethodのシグネチャーと比較する部分と、切込み点(pointcut)のシグネチャー(切込み点(pointcut)の名前又はIDとして使われる)からなります。シグネチャーの戻り値はvoidでなければなりません。

対象のmethodのシグネチャーと比較する部分の規則は付録 Aを参照下さい。

例の場合、切込み点(pointcut)の名前はpointcut1です。someMethodという名前のメソッドに合致します。

 

4.2.3 勧告(advice)

(1)before

 

リスト4.2-6 before勧告(advice)アノテーション

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

@Aspect
public class Aspect1{

① @Before("jp.co.xyz.app1.Pointcuts.pointcut1()")
  public void doSomethingBefore1() {
    // ...
  }

② @Before("execution(* someMethod(..))")
  public void doSomethingBefore2() {
    // ...
  }
}

 

勧告(advice)は様相(aspect)の定義の中で設定します。一つの様相(aspect)に複数の勧告(advice)を指定できます。これらの勧告は同じ型の勧告である必要はありません。

使う切込み点(pointcut)は@Beforeアノテーションで指定します。定義済みの切込み点(pointcut)をidで参照する方法と直接定義を書く方法があります。①は参照する方法、②は直接定義する方法を使っています。

切込み点(pointcut)を指定するこの二つの方法は他の型の勧告についても同じです。他の説明では参照する方法だけを例示します。

 

(2)after returning

 

リスト4.2-7 after returning勧告(advice)アノテーション

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterReturning;

@Aspect
public class Aspect1{

① @AfterReturning("jp.co.xyz.app1.Pointcuts.pointcut1()")
  public void doSomethingAfterReturning1() {
    // ...
  }

② @AfterReturning(
    pointcut="jp.co.xyz.app1.Pointcuts.pointcut1()",
    returning="ret")
  public void doSomethingAfterReturning2(Object ret) {
    // ...
  }
}

 

指定の方法はbefore勧告(advice)と同じです。ただし、戻り値を取り出す設定ができます。①は戻り値を取り出す設定はしていません。②は戻り値を取り出す設定(returning属性)をしています。引数の名前を指定します。returningで指定した値(rtn)は、このアノテーションが付いているメソッド(doSomethingAfterReturning2)の引数の名前に似ています。

 

(3)around

 

リスト4.2-8 around勧告(advice)アノテーション

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.ProceedingJoinPoint;

@Aspect
public class Aspect1{

  @Around("jp.co.xyz.app.Pointcuts.pointcut1()")
  public Object doSomethingAround(ProceedingJoinPoint pjp) throws Throwable {
    // start stopwatch

    Object retVal = pjp.proceed();

    // stop stopwatch
    return retVal;
  }
}

 

指定の方法はbefore勧告(advice)と同じです。aroudはbeforeとafter-returningを兼ねています。before又はafter-returningで済む場合は、aroundを使わないでそちらを使うべきです。

around勧告(advice)の第1引数はProceedingJoinPointです。

 

(4)after throwing

 

リスト4.2-9 after throwing勧告(advice)アノテーション

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;

@Aspect
public class Aspect1{

① @AfterThrowing("jp.co.xyz.app1.Pointcuts.pointcut1()")
  public void doSomethingThrowing1() {
    // ...
  }

② @AfterThrowing(
    pointcut="jp.co.xyz.app1.Pointcuts.pointcut1()",
    throwing="ex")
  public void doSomethingThrowing2(MyException1 ex) {
    // ...
  }
}

 

指定の方法はbefore勧告(advice)と同じです。ただし、投げられた例外オブジェクトを勧告の引数として取り出す設定ができます。①は例外オブジェクトを取り出す設定はしていません。②は例外オブジェクトを取り出す設定(throwing属性)をしています。引数の名前を指定します。この場合、doSomethingThrowing2の引数は次の形になります。引数で指定した例外のクラスに一致した例外が投げられた場合に該当する勧告(advice)に制御が移ります。

 

(5)after(finally)

 

リスト4.2-10 after勧告(advice)アノテーション

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.After;

@Aspect
public class Aspect1{

  @After("jp.co.xyz.app1.Pointcuts.point1()")
  public void doSomethingAfter() {
    // ...
  }
}

 

after勧告(advice)は正常に戻る場合でも例外で戻る場合でも実行される勧告(advice)です。after-returning勧告(advice)は正常に戻る場合だけしか実行されません。

 

例題3はxsd-AOPを使った例です。

 

4.3 Spring2.0の導入(introduction)機能

導入(introduction)の機能も、xsd-AOPまたは@Aspect-AOPで変更されています。

導入(introduction)を構成する要素は次の通りです。

(1) 基礎関心事処理クラス

(2) 勧告(advice)

(3) 導入(introduction)されるinterface

(4) 導入(introduction)を実装したクラス

 

Spring1.2では、導入(introduction)されるインタフェースは勧告(advice)に実装しなければなりませんでした。Spring2.0ではこの点が緩和されて、勧告(advice)と導入(introduction)されるinterfaceの実装を同じクラスに含めなくてもよくなっています。

さらに進んで、勧告(advice)と導入(introduction)が完全に分離されています。つまり、Spring1.2では導入(introduction)といっしょに使える勧告(advice)の型(結合点(join points)の型)はaround型だけでした。Spring2.0では、導入(introduction)と勧告(advice)は分離されましたので、導入(introduction)と組み合わせて任意の型の勧告(advice)が使えます。

 

xsd-AOPの場合、アプリケーションコンテキストの中で、リスト4.3-1に示す形で、これら4個の情報が指定されます。

 

リスト4.3-1 xsd-AOPを使った導入(introduction)の設定

  <aop:config>
    <aop:aspect id="aspect1" ref="lockMixinAdvice">
        <aop:declare-parents
            types-matching="msh.pub.springaop.intro_xml.impl.Service"
            implement-interface="msh.pub.springaop.intro_xml.ILockable"
            default-impl="msh.pub.springaop.intro_xml.impl.Lockable" />
        <aop:around
            pointcut="execution(* msh.pub.springaop.intro_xml.IService.set*(..))"
            method="invoke"
            arg-names="pjp"/>
            
    </aop:aspect>
  </aop:config>
  ...
  <bean id="lockMixinAdvice" class="msh.pub.springaop.intro_xml.impl.LockMixinAdvice"/>

 

これはAspectJアノテーションを使って設定することも出来ます。これをリスト4.3-2にこれを示します。

 

リスト4.3-2 @Aspectを使った導入(introduction)の設定

...
@Aspect
public class LockMixinAdvice{

    @DeclareParents(value="msh.pub.springaop.intro_an.impl.Service",
               defaultImpl=Lockable.class)
    public static ILockable lockable;

    @Around("execution(* msh.pub.springaop.intro_an.IService.set*(..))")
    public void invoke(ProceedingJoinPoint pjp) throws Throwable {
        ILockable lockable = (ILockable)pjp.getThis();
        if (lockable.locked()){
            // something to do
            Signature signature = (Signature)pjp.getSignature();
            throw new LockedException("Exception " + signature.getName() + " is locked.");
        }
        Object rval = pjp.proceed();
        ...
    }
}

 

 

例題はソース中の次のパッケージを参照下さい。

(1)msh.pub.springaop.intro_xsd : xsd-AOPによる導入(introduction)を実装

(2)msh.pub.springaop.intro_an : @Aspect-AOPによる導入(introduction)を実装

例題の内容はいずれもSpring1.2の導入(introduction)の例に使ったものと同じです。

 

 

5. 引数付き勧告(advice)

これまでの例題では勧告(advice)の引数はなしでした。この章では引数付きの勧告(advice)を考えます。説明の例は@Aspect-AOPを使います。xsd-AOPでも設定でき、内容はほぼ1:1に対応します。xsd-AOPを使う説明は付録を参照下さい。

なお、dtd-AOPの場合は、勧告(advice)のメソッドのシグネチャーは、勧告(advice)の型に対応して固定されています。この引数から勧告されるメソッドの引数を取り出せます。勧告(advice)を担うメソッドのの引数を設定する自由度はありませんので、dtd-AOPについてはここでは説明しません。

 

5.1 メソッドの引数

勧告(advice)されるメソッド(=ターゲットのメソッド)に渡す引数を勧告に渡すことが出来ます。

 

例えば、次のメソッドを考えます。

 

リスト5.1-1 例に使うメソッドのシグネチャ

    public int calcTotal(String[] names);

 

引数namesを勧告(advice)に渡す方法は次の通りです。

 

リスト5.1-2 引数を受け取るための@Beforeアノテーション

    @Before("execution(public * msh.pub.springaop.simplecmn.IService.calcTotal(..)) &&"
            + " args(theNnames)")
    public void before(String[] theNames){
        ...
    }

 

引数がない場合の設定と違う点は && arg(theNnames)があることです。

&&は論理演算子です。Javaの&&と同じ意味です。

argsは次の意味を持ちます。

(1)引数が1個

(2)その引数の型がString[]

(3)勧告(advice)に該当のメソッド(例ではcalcTotal)の引数が渡される。

 

切込み点(pointcut)があらかじめ定義されている場合の設定は次の通りです。

 

リスト5.1-3 切込み点(pointcut)アノテーションを参照する@Beforeアノテーション

    @Pointcut("execution(* msh.pub.springaop.simplecmn.IService.*(..)) && args(theNames)")
    public void beforePointcut(String[] theNames) {}
    ...
    @Before("beforePointcut(theNames)")
    public void before(String[] theNames){
        ...
    }

 

@Pointcutアノテーションで指定する内容は、@Beforeで指定する内容と同じです。argsも必要です。切込み点の名前を意味するメソッドの引数は、argsの指定に対応して指定します。

@Beforeアノテーションはこの@Pointcutアノテーションを参照する形で指定します。@Pointcutの名称は引数を含めて指定します。ただし、引数の型は要りません。

 

例として@Beforeの場合を挙げましたが、他の結合点(joint points)の型でも同じ方法で指定できます。

ただし、@Aroundの場合は、第1引数は必須です。これは切込み点(pointcut)の中にargsで指定しなくても渡されます。逆にいうと、勧告(advice)の仮引数として必須です。これを次に示します。

 

リスト5.1-4 aroud勧告(advice)の第1引数

    @Around("execution(public * calcTotal(..))")
    public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
        ...
        Object rval = pjp.proceed();
        ...
        return rval;
    }

 

第1引数以外に、メソッドの引数を渡す場合は、argsで指定する必要があります。方法は@Beforeと同じです。argsに指定する引数には第1引数(pjp)は含めません。

 

5.2 結合点(join point)情報

勧告(advice)に入ったときの結合点(join point)の情報を渡すことができます。結合点の情報を含むオブジェクトの型(interface)はJoinPoint(*1)です。JoinPointを受け取る場合は必ず第1引数でなければなりません。

なお、@Aroundの場合の第1引数は必ずProceedingJoinPoint(*1)です。これはJoinPointを継承した型です。

注 *1:パッケージはorg.aspectj.langです。

 

勧告(advice)に渡す引数の指定の形式はProceedingJoinPointもJoinPointも同じです。

 

リスト5.2-1 結合点(join point)情報を受け取る@Beforeアノテーション

    @Before("execution(public * msh.pub.springaop.simplecmn.IService.*(..))"
         + " && args(theNames)")
     public void before(JoinPoint jp,String[] theNames){

 

内容は引数JoinPointがないときと同じです。特にargsに引数JoinPointを含めないことに注意して下さい。

 

JoinPointの主メソッドを次に列挙します。

(1)getArgs(): 引数の情報

(2)getThis(): proxyの情報

(3)getTarget(): ターゲット(基礎関心事処理クラス)の情報

(4)getSignature(): 勧告(advice)されるメソッドの情報

 

5.3 戻り値

結合点(join point)の型がafterReturningの場合は、戻り値を勧告(advice)の引数として取得できます。

指定の仕方は次の通りです。要素名returningで指定します。切込み点(pointcut)を指定するargsには戻り値に対応する変数名は書きません。

 

リスト5.3-1 戻り値を受け取る@AfterReturningアノテーション

    @AfterReturning(
         pointcut="execution(public * calcTotal(..)) && args(theName)"
        ,returning="ret")
    public void afterReturning(JoinPoint jp,int ret,String[] theNames){
    ...
    }

 

after型の勧告(advice)の場合は戻り値を取得できません。

around型の場合も戻り値を引数として取得できません。しかし、勧告(advice)の中でメソッドを呼び出しますので、戻り値を勧告(advice)の中で使えます。

 

5.4 例外

結合点(join point)の型がafterThrowingの場合は、例外を勧告(advice)の引数として取得できます。

指定に仕方は次の通りです。要素名throwingで指定します。切込み点(pointcut)を指定するargsには戻り値に対応する変数名は書きません。

 

リスト5.4-1 例外を受け取る@AferThrowing

    @AfterThrowing(
         pointcut="afterThrowingPointcut() && args(theNames)"
        ,throwing="ex")
    public void afterThrowing1(JoinPoint jp, MyException1 ex, String[] theNames) {
    ...
    }

 

after型の勧告(advice)は例外を取得できません。

 

5.5 引数の対応

メソッドの引数と勧告(advice)の引数の対応付けは正常にできるとは限りません。通常、ソース上の変数名はを実行時に残っていません。残すにはそのための手当てが必要ですがそれについてはこの解説では述べません。引数を勧告(advice)に渡す場合は全引数を順序を変えずに渡す原則で臨むことです。たとえ、勧告(advice)に全引数が必要でなくともです。

 

5.6 引数の加工

結合点(join point)の型がaround型の場合は、メソッドに渡す引数を勧告(advice)内で生成又は加工できます。順序と型が一致していなければなりません。

例を次に示します。

 

リスト5.6-1 引数を加工するaround勧告(advice)

    @Around(
            value="execution(public * registerNewMember(..)) && args(theName, theIndex)"
           ,argNames="pjp,theName,theIndex")
    public Object invoke2(ProceedingJoinPoint pjp,String theName, int theIndex)
          throws Throwable {
           ...
        // change args
           String newName = "*" + theName + "*";
           int newIndex = 2* theIndex;
           
           Object rval = pjp.proceed(new Object[]{newName, new Integer(newIndex)});
           ...
        return rval;
    }

 

この例題では、勧告(advice)されるメソッドの引数は(String,int)としています。変更した後、これらをObject型の配列としてproceedを呼び出しています。どの引数も変更しない場合は、proceedの引数は空でいいことに注意してください。

 

 引数付きの例題は例題4と例題5を参照下さい。

 

 

 

6.例題1

6.1 概要

ソース中のパッケージ名:msh.pub.springaop.simple

AOPを説明するのに使った例を母体にした、Spring1.2AOPの例題です。後の章で同じ例題をxsd-AOPと@Aspect-AOPでも実装します。

クラス構成を図6.1-1に示します。

 

図6.1-1 クラス図(p35)

図6.1-1 クラス図

 

DBアクセスを念頭に置いた構造にし、Daoクラスを設けます。DaoクラスはDBアクセスを想定しています。しかし、例題では実際のDBMSは使わず、DaoクラスはデータをMap型で保持します。Map型は(key,value)値を保持するデータ型です。keyはString型、valueはInteger型にします。例題では、これをstaticブロックで初期化します。

Daoはこのデータを扱う二つのメソッドをを持っています。

(1)addRecord(String key, int value)

   一組の(key,value)を登録します。

    key:キー値

    key:整数値

    この例題ではaddRecordは使いませんので処理は実装していません。

(2)getData(key)

    key:キー値

    戻り値:キー値に対応している整数値

    例外:次の2種の例外を投げます。

      (a)keyに相当するデータがないとき

        MyException1を投げます。

      (a)keyがnullのとき

        MyException2を投げます。

 

全体の処理は、Clientで与えた(0以上)のキーに対応する整数の合計を出すものです。キーの数が2個の場合の処理のシーケンスを図6.1-2に示します。

図6.1-2 処理のシーケンス(p36)

図6.1-2 処理のシーケンス

 

AOPのテストは、Serviceを基礎関心事処理(ターゲット)として行います。そのクラス構成を図6.1-3に示します。

図6.1-3 AOPを使った構成(p36)

図6.1-3 AOPを使った構成

 

勧告(advice)として、AdviceInterceptorを加えます。この型の勧告(advice)は3章の説明に使った例題では出てきませんでした。この例題ではAdviceInterceptorを加えて、次の4種の勧告(advice)を調べます。

(1)AdviceBefore:あるメソッドの開始直前に実行される。

(2)AdviceAfter:あるメソッドの処理終了直後に実行される。

(3)AdviceInterceptor:あるメソッドの開始直前と処理終了直後の両方で実行される。

(4)AdviceThrowing:あるメソッドの処理が例外で戻る際に実行される。

 

:AdviceBeforeとAdviceAfter、AdviceThrowingはその処理の中で、勧告(advice)されるメソッドを意識する必要はありません。これに対してAdviceInterceptorはその処理の中に勧告(advice)されるメソッドを呼び出す処理を含みます。この場合もメソッドの名前で呼び出すわけではありません。

 

performに相当するメソッドはperform1とperform2、perform3の3個あります。条件を変えてAOPの機能を調べるためです。

(1) perform1

キーとして正しいキーを与えます。正常終了する場合です。

AdviceBefore、AdviceAfter、AdviceInterceptorが実行される。なお、AdviceInterceptorはAdviceBeforeの前とAdviceAfterの後の実行される。

(2) perform2

データに含まれないキーを与えます。MyException1を投げるテストです。

AdviceBeforeとAdviceInterceptorの呼び出し前とAdviceThrowingが使われます。引数が異なる二つのafterThrowingメソッドがあります。このテストは引数が1個の場合です。

(3) perform3

getDataの引数(キー)をnullにします。MyException2を投げるテストです。

AdviceBeforeとAdviceInterceptorの呼び出し前とAdviceThrowingが使われます。このテストはafterThrowingメソッドの引数が4個の場合です。

 

なお、この例題の勧告(advice)に関係する結合点(join points)の型はそれぞれ異なりますので、adviceを一つのクラス(様相(aspect))にまとめることが出来ます。すべての勧告(advice)を一つの様相(aspect)に含めた例は、ソース(データ名simple_1aspect)を参照下さい。

 

 

7.例題2

7.1 概要

ソース中のパッケージ名:msh.pub.springaop.simple_xsd

例題1をスキーマに基づくAOPサポートを使って実装します。スキーマに基づくAOPサポートでは、次の結合点(join points)も使えます。

・メソッドから戻る直前(呼び出されたメソッドが例外を投げた場合も投げない場合も該当する)

これも例題に組み入れます。

 

アプリケーションコンテキストを少しずつ見て行きます。

 

7.1.1 基礎関心事処理クラス

 

リスト7.1-1基礎関心事処理クラス

  <bean id="theClient" class="msh.pub.springaop.simple_xsd.impl.Client">
      <property name="service" ref="theService" />
  </bean>

 

AOPを使わない場合と変わらない設定です。ServiceとDaoの設定も同じように出来ますので省略しています。

注意する点は、serviceプロパティにproxy(のid)を設定していましたが、xsd-AOPを使う場合は、基礎関心事処理クラスのbean(のid)をここに設定します。

 

7.1.2 勧告(advice)

 

リスト7.1-2 勧告(advice)クラス

  <bean id="adviceBefore" class="msh.pub.springaop.simple_xsd.impl.AdviceBefore"/>
  <bean id="adviceAfter" class="msh.pub.springaop.simple_xsd.impl.AdviceAfter"/>
  <bean id="adviceThrowing" class="msh.pub.springaop.simple_xsd.impl.AdviceThrowing"/>

 

勧告(advice)クラスはAOPに関係していますが、その設定は通常のクラスと変わりません。この点はdtd-AOPと変わりません。

ただし、具体的なクラスの内容は違っています。dtd-AOPの場合は、Springが提供するinterfaceを実装しなければなりませんでした。その結果、メソッド名は固定されていました。

xsd-AOPでは、自由にメソッド名は決められます。しかし、この例題はdtd-AOPと比較しやすくできるようにするため、メソッド名を同じにしています。

 

7.1.3 Advisor(Aspect)

 

リスト7.1-3 Advisor(Aspect)

  <aop:config>
    <aop:aspect id="advisorBefore" ref="adviceBefore">
        <aop:before
            pointcut="execution(public * msh.pub.springaop.simplecmn.IService.*(..))"
            method="before"/>
    </aop:aspect>
    ...
  </aop:config>

 

この例題ではdtd-AOPと比較しやすくするため、”Advisor”に相当する形をを残しました。その点では新しい機能が活かされていません。

Advisorは一つのクラスに一つの勧告(advice)しか含められません。xsd-AOPはこの点に関して一般化されて一つのクラス(様相(aspect))に複数の勧告(advice)を含められます。例題では、Advisorと同じ一つのクラス(様相(aspect))に一つの勧告(advice)にしています。

上記のコードは、before勧告を含んだ様相(aspect)の定義です。

この中で次の情報が設定されています。

(1)様相(aspect):aop:aspect要素のref属性

(2)結合点(join points)の型:aop:aspectの中の要素aop:before

(3)切込み点(pointcut):aop:before要素のpointcut属性

(4)勧告(advice)を担うメソッド:、aop:before要素のmethod属性

 

7.1.4 proxy

該当する設定はありません。自動的にproxyが作られます。それを指示するために次の要素をアプリケーションコンテキストに含めます。

 

リスト7.1-4 proxyクラス

<aop:aspectj-autoproxy/>

 

7.1.5 基礎関心事処理を使うクラス

 

リスト2.3-5 基礎機能を使うクラス

  <bean id="theClient" class="msh.pub.springaop.simplecmn.impl.Client">
      <property name="service" ref="theService" />
  </bean>

 

基礎関心事処理を使うクラスはAOPを使わない通常のクラスですから、その指定も通常通りです。ただ、dtd-AOPではServiceの参照を設定するプロパティに”proxy”を設定しましたが、@Aspect-AOPではServiceのbeanのid(theService)を設定します。

例題を分かりやすくするために、一つの様相(aspect)に一つの勧告(advice)だけ含めました。すべての勧告(advice)を一つの様相(aspect)に含めた例は、ソース(データ名simple_xsd_1aspect)を参照下さい。

 

 

8.例題3

8.1概要

ソース中のパッケージ名:msh.pub.springaop.simple_an

前の例題をAspectJアノテーションを使って書き直します。

アプリケーションコンテキストの中のaop:config要素で定義していた様相(aspect)、さらにその中で定義された勧告(advice)の情報をJavaのアノテーション機能を使って定義します。したがって、xsd-AOPで使ったaop:configの内容は不要になります。アプリケーションコンテキストのその他の内容はまったく同じです。

 

8.1.1 様相(aspect)

様相(aspect)のクラスをリスト8.1-1に示します。

 

リスト8.1-1 様相(aspect)

...
@Aspect
public class AdviceBefore{

    @Before("execution(public * msh.pub.springaop.simplecmn.IService.*(..))")
    public void before(){
        // somthing to do
    }
    ...
}

 

このリストの注目する点は次の2点です。

(1) @Aspect

(2) @Before

 

@Aspectはこのクラスが様相(aspect)用のクラスであることを意味するアノテーションです。

@Beforeは直後のメソッドが勧告(advice)であることを意味します。、結合点(join points)の型はBeforeです。切込み点は、@Beforeの要素で示されています。この場合は、msh.pub.springaop.simple_an.IServiceのpublicなメソッドが対象になります。

 

この例では、様相(aspect)に一つの勧告(advice)しかありません。これは、他の例題と比較しやすくするためです。実際には複数個の勧告(advice)を一つの様相(aspect)クラスに定義できます。

 

8.1.2 切込み点(pointcut)

リスト8.1-2は切込み点(pointcut)を違った形で定義した例です。

 

リスト8.1-2 切込み点(pointcut)の定義

...
@Aspect
public class AdviceThrowing{
    private String className = this.getClass().getSimpleName();

    @Pointcut("execution(* msh.pub.springaop.simplecmn.IService.*(..))")
    public void afterThrowingPointcut() {}

    @AfterThrowing("afterThrowingPointcut()")
    public void afterThrowing0() {
        // something to do
    }

    @AfterThrowing(
            pointcut="afterThrowingPointcut()",
            throwing="ex")
    public void afterThrowing1(MyException1 ex) {
        // something to do
    }
    ...
}

 

これは切込み点(pointcut)をあらかじめ定義して、勧告(advice)のアノテーションではそれを参照します。

@Pointcutは切込み点(pointcut)を定義するアノテーションです。

@AfterThrowingは例外に対応する勧告(advice)を定義します。その引数で、前に定義した切込み点の名前(シグネチャー)が使われています。

 

例題を分かりやすくするために、一つの様相(aspect)に一つの勧告(advice)だけ含めました。全勧告(advice)を一つの様相(aspect)に含めた例は、ソース(データ名simple_an_1aspect)を参照下さい

 

 

9.例題4

9.1 概要

ソース中のパッケージ名:msh.pub.springaop.simple_an_arg

引数付き勧告(advice)の例題を@Aspect-AOPで実装します。クラスの構成は例題1と同じです。

勧告(advice)に次の引数を渡します。

(1)before

  (a)public void before(String[] theNames)

     theNames:勧告されるメソッド(calcTotal)の第1引数

  (b)public void before(JoinPoint jp,String[] theNames)

     jp:beforeに制御を移した結合点(join point)の情報を持つオブジェクト

     theNames:は勧告されるメソッド(calcTotal)の第1引数

 

(2)after returning

    public void afterReturning(JoinPoint jp,int ret,String[] theNames)

      jp: afterReturningに制御を移した結合点(join point)の情報を持つオブジェクト

      ret:戻り値

      theNames:は勧告されるメソッド(calcTotal)の第1引数

 

(3)around

    public Object invoke(ProceedingJoinPoint pjp,String[] theNames)

      pjp: afterReturningに制御を移した結合点(join point)の情報を持つオブジェクト(必須)

      theNames:は勧告されるメソッド(calcTotal)の第1引数

 

(4)afterThrowing

    public void afterThrowing1(JoinPoint jp, MyException1 ex, String[] theNames)

      jp: afterReturningに制御を移した結合点(join point)の情報を持つオブジェクト

      ex:例外オブジェクト

      theNames:は勧告されるメソッド(calcTotal)の第1引数

    

(5)after

    public void after(JoinPoint jp,String[] theNames)

     jp:beforeに制御を移した結合点(join point)の情報を持つオブジェクト

     theNames:は勧告されるメソッド(calcTotal)の第1引数

 

以上の(1)~(5)の勧告(advice)を受けるメソッドはcalcTotalです。

この例題では、この他にaround型で引数を加工する例を追加します。その勧告のシグネチャは次の通りです。

 

    public Object invoke2(ProceedingJoinPoint pjp,String theName, int theIndex)

      pjp: afterReturningに制御を移した結合点(join point)の情報を持つオブジェクト(必須)

      theName:は勧告されるメソッド(registerNewMember)の第1引数

      theIndex:は勧告されるメソッド(registerNewMember)の第2引数

 

theNameとtheIndexに簡単な処理をして、それを引数としてregisterNewMemberを呼び出します。

 

9.2 アノテーション

9.2.1 before

 

リスト9.2-1 @Beforeアノテーション

① @Before("execution(public * msh.pub.springaop.simplecmn.IService.*(..)) &&"
            + "args(theNames)")
    public void before(String[] theNames){
        ...
    }
    ...
② @Pointcut("execution(* msh.pub.springaop.simplecmn.IService.*(..)) && args(theNames)")
    public void beforePointcut(String[] theNames) {}

③ @Before("beforePointcut(theNames)")
    public void before(JoinPoint jp,String[] theNames){
        ...
    }

①は勧告されるメソッドの引数を受け取る指定です。

③はそれに加えて結合点(join point)の情報も受け取ります。

②は③で使う結合点(pointcut)を定義するアノテーションです。

 

9.2.2 after returning

 

リスト9.2-2 @AfterReturningアノテーション

    @AfterReturning(
         pointcut="execution(public * calcTotal(..)) && args(theNames)"
        ,returning="ret")
    public void afterReturning(JoinPoint jp,int ret,String[] theNames){
        ...
    }

結合点(join point)と戻り値と勧告されるメソッドの引数を受け取ります。

 

 

9.2.3 around

 

リスト9.2-3 @Aroundアノテーション

① @Around("execution(public * calcTotal(..)) && args(theNames)")
    public Object invoke(ProceedingJoinPoint pjp,String[] theNames) throws Throwable {
        ...
    }
    ...
② @Around("execution(public * registerNewMember(..))"
         + " && args(theName, theIndex)")
    public Object invoke2(ProceedingJoinPoint pjp,String theName, int theIndex) throws Throwable {
        ...
        // change args
③ String newName = "*" + theName + "*";
④ int newIndex = 2* theIndex;
⑤ Object rval = pjp.proceed(new Object[]{newName, new Integer(newIndex)});
        ...
    }

①は勧告されるメソッドの引数を受け取ります。第1引数(pjp)は必須です。

②は受け取った引数を変更して、勧告(advice)を受けるメソッドを呼び出す例です。

③と④で引数に加工を加えます。

⑤は加工後の値をObject配列の要素として目的のメソッドを呼び出しています。

 

9.2.4 after throwing

 

リスト9.2-4 @AfterThrowingアノテーション

    @AfterThrowing(
         pointcut="afterThrowingPointcut() && args(theNames)"
             ,throwing="ex")
    public void afterThrowing1(JoinPoint jp, MyException1 ex, String[] theNames){
        ...
    }

結合点の情報と例外と勧告されるメソッドの引数を受け取ります。

 

9.2.5 after

 

リスト9.2-5 @Afterアノテーション

    @After("execution(public * calcTotal(..)) && args(theNames)")
    public void after(JoinPoint jp,String[] theNames){
        ...
    }

勧告されるメソッドの引数を受け取ります。

 

 

 

10.例題5

10.1 概要

ソース中のパッケージ名:msh.pub.springaop.simple_xsd_arg

内容は例題4と同じです。@xsd-AOPで実装します。

 

10.2 aop:config

10.2.1 before

リスト10.2-1 before勧告(advice)の定義

  <aop:config>
    ...
    <aop:aspect id="advisorBefore" ref="adviceBefore">

① <aop:before
            pointcut="execution(public * msh.pub.springaop.simplecmn.IService.calcTotal(String[])) and args(names)"
            method="before1"
            arg-names="names"/><!-- BUG using a method with the same name, error -->

② <aop:before
            pointcut="execution(public * msh.pub.springaop.simplecmn.IService.calcTotal(String[])) and args(names)"
            method="before2"
            arg-names="jp,names"/>
    </aop:aspect>
    ...
  </aop:config>

①は勧告されるメソッドの引数を受け取る指定です。pointcut属性の指定の仕方は@Aspect-AOPと同じです。arg-namesで勧告(advice)の引数名を指定します。これは@Aspect-AOPの場合は勧告(advice)のメソッドの引数から得られる情報です。

②は勧告されるメソッドの引数に加えて結合点(join point)の情報も受け取ります。そのための引数jpはpointcut属性のargsには指定しない点は@Aspect-AOPと同じです。arg-namesにはjpを含めて指定します。

 

バグ 参照 付録C.2 

 

10.2.2 after returning

 

リスト10.2-2 after returninig勧告(advice)の定義

    <aop:aspect id="advisorAfterReturning" ref="adviceAfterReturning">
        <aop:after-returning
            pointcut="execution(public * calcTotal(..)) and args(theNames)"
            method="afterReturning"
            returning="ret"
            arg-names="jp,ret,theNames"/>
    </aop:aspect>

結合点(join point)と戻りと勧告されるメソッドの引数を受け取ります。戻り値の変数名はreturning属性で指定します。arg-namesでは結合点(join point)(jp)情報と戻り値(ret)も指定します。pointcut属性のargsにはこれらは指定しません。

 

10.2.3 around

 

リスト10.2-3 around勧告(advice)の定義

① <aop:aspect id="advisorInterceptor" ref="adviceInterceptor">
        <aop:around
            pointcut="execution(public * calcTotal(..)) and args(theNames)"
            method="invoke"
            arg-names="pjp,theNames"/>

② <aop:around
            pointcut="execution(public * registerNewMember(..)) and args(theName, theIndex)"
            method="invoke2"
            arg-names="pjp,theName,theIndex"/>
    </aop:aspect>

①は結合点(join point)の情報と勧告されるメソッドの引数を受け取ります。第1引数(pjp)は必須です。pjp以外に引数がない場合は、arg-namesを省略できます。

②は二つの引数を受け取ります。この二つの引数を加工した後、勧告(advice)を受けるメソッドに渡す例ですが、この指定にはその処理に関係した設定は現れません。

 

10.2.4 after throwing

 

リスト10.2-4 after throwing勧告(advice)の定義

    <aop:aspect id="advisorThrowing0" ref="adviceThrowing">
        <aop:after-throwing
            pointcut="execution( * msh.pub.springaop.simplecmn.IService.calc*(..)) and args(theNames)"
            method="afterThrowing1"
            throwing="ex"
            arg-names="jp,ex,theNames"/>
    </aop:aspect>

結合点の情報と例外と勧告されるメソッドの引数を受け取ります。

 

10.2.5 after

 

リスト10.2-5 after勧告(advice)の定義

    <aop:aspect id="advisorAfter" ref="adviceAfter">
        <aop:after
            pointcut="execution(public * calcTotal(..)) and args(theNames)"
            method="after"
            arg-names="jp,theNames"/>
    </aop:aspect>

結合点の情報と勧告されるメソッドの引数を受け取ります。

 

 

11.資料

(1)Chapter 7. Spring AOP APIs

    http://www.springframework.org/docs/reference/aop-api.html

(2)Chapter 6. Aspect Oriented Programming with Spring

    http://www.springframework.org/docs/reference/aop.html

(3)Chapter 3. The IoC container

    http://www.springframework.org/docs/reference/beans.html#beans-factory-collaborators

 

付録

付録 A.切込み点(pointcut)の指定構文

下記の資料を参考に切込み点(pointcut)を指定する構文を紹介します。

ref: http://www.springframework.org/docs/reference/aop.html

よく使われる構文だけ紹介します。

 

A.1 execution

(1)構文

execution(

    modifiers-pattern?

    ret-type-pattern

    declaring-type-pattern?

    name-pattern(param-pattern)

    throws-pattern?)

executionの()は一つの文字列で指定します。

ret-type-pattern以外はオプションです。他の項目は必須です。

ret-type-pattern:戻り値の型。*は何の型にも一致することを意味します。

declaring-type-pattern:完全クラス名(パッケージ名+クラス(interface)名)のパターン。これはオプションですが、指定する場合は、次のname-patternと「.」でつなぐ。

name-pattern:メソッド名。名前の全体又は一部に*を使うことが出来ます。

declaring-type-patternとname-patternを合わせたパターンの例

    jp.co.abc.IService.setStringList(String s)に一致する記述

        *.*.*.IService.setStringList(*)

        *.setStringList(*)

    jp.co.abc.IService.setStringList(String s)に一致しない記述

        *.IService.setStringList(*)

 

要約する次のことがいえます。完全タイプ名(パッケージ名を含むクラス名又はインタフェース名)は一つの*と一致するが、*.IServiceのように完全タイプ名の一部を*で表現できない。ただし、*.*.*.IService.setStringList(*)のようにパッケージの各項は一つの*に一致する。

 

(param-pattern):引数のパターン。

    () 引数がない。

    (..) 引数の数に制限なし。なくてもよい。

    (*) 引数の数は1個。型は任意

    (*,String) 引数の数は2個。第1引数は型は任意。第2引数の型はString

 

(2)例

  (a) execution(public * *(..)) publicなすべてのメソッド

  (b) execution(* set*(..)) メソッド名がsetで始まるすべてのメソッド

  (c) execution(* jp.co.xyz.app1.CalcService.*(..)) CalcService interfaceに含まれるメソッド

 

A.2 within

(1)within(type-pattern)

type-patternで指定された型に一致します。

(2)例

  within(jp.co.xyz.app1.*) パッケージがjp.co.xyz.app1である型に一致します。

  within (jp.co.xyz.app1..*) パッケージがjp.co.xyz.app1.aaaでaaaが任意の型に一致します。

 

A.3 this

(1)this(type-pattern)

type-patternで指定されたinterfaceをproxyが実装している場合に一致します。

(2)例

  this(jp.co.xyz.app1.*) パッケージがjp.co.xyz.app1である型に一致します。

  this(jp.co.xyz.app1..*) パッケージがjp.co.xyz.app1.aaaでaaaが任意の型に一致します。

 

A.4 target

(1)target(type-pattern)

type-patternで指定されたinterfaceをtarget(基礎関心事処理のクラス)が実装している場合に一致します。

(2)例

  target (jp.co.xyz.app1.*) パッケージがjp.co.xyz.app1である型に一致します。

  target (jp.co.xyz.app1..*) パッケージがjp.co.xyz.app1.aaaでaaaが任意の型に一致します。

 

A.5 args

(1)args(type-pattern1, type-pattern2,...)

type-patternで指定された引数である場合に一致します。

(2)例

  arg (String) パッケージがjp.co.xyz.app1である型に一致します。

 

 

付録 B. xsd-AOPによる引数付き勧告(advice)

9章の@Aspectによる引数付き勧告(advice)は読まれた前提で書いています。

 

B.1 メソッドの引数

引数namesを勧告(advice)を渡す方法は次の通りです。

 

リスト B.1-1 引数を受け取るbefore勧告(advice)の定義

    <aop:aspect id="advisorBefore" ref="adviceBefore">
        <aop:before
            pointcut="execution(...) and args(names)"
            method="before1"
            arg-names="names"/><!-- BUG using a method with the same name, error -->
    </aop:aspect>

 

次の勧告(advice)に対応します。

 

リスト B.1-2 引数を受け取るbefore勧告(advice)メソッド

    public void before1(String[] theNames){
        ...
    }

 

これはaop:beforeの例ですが、他の結合点(join point)についても同じです。@Aspect異なる点は、&&がandに変わり、アノテーションの要素名argNamesがarg-names属性になることです。

aroundの場合は、第1引数が固定されています。例を次に示します。

 

リスト B.1-3 引数を受け取るaround勧告(advice)の定義

        <aop:around
            pointcut="execution(...) and args(theNames)"
            method="invoke"
            arg-names="pjp,theNames"/>
    }

 

第1引数はargsには指定せず、arg-namesには指定します。pjp以外に引数がない場合はarg-namesは要りません。

 

B.2 結合点(join point)情報

 

リスト B.2-1 結合点(join point)情報を受け取るbefore勧告(advice)の定義

    <aop:aspect id="advisorBefore" ref="adviceBefore">
        <aop:before
            pointcut="execution(...) and args(names)"
            method="before2"
            arg-names="jp,names"/>
    </aop:aspect>

 

aop:beforeの属性pointcutのargsで指定する変数(引数)には、結合点(join point)の情報に対応する引数(jp)は含めません。pointcut属性で指定するのは勧告(advice)されるメソッドに関する情報です。これにはjpは含まないからです。before以外の他の型の結合点(join points)でもargsの指定の仕方は同じです。

 

上記の指定は次の勧告(advice)に対応します。

 

リスト B.2-2 結合点(join point)情報を受け取るbefore勧告(advice)メソッド

    public void before2(JoinPoint jp,String[] theNames){
        ...
    }

 

B.3 戻り値

 

リスト B.3-1 戻り値を受け取るafter returning勧告(advice)の定義

    <aop:aspect id="advisorAfterReturning" ref="adviceAfterReturning">
        <aop:after-returning
            pointcut="execution(public * calcTotal(..)) and args(theNames)"
            method="afterReturning"
            returning="ret"
            arg-names="jp,ret,theNames"/>
    </aop:aspect>

 

aop:beforeの属性pointcutのargsで指定する変数(引数)には、戻り値に対応する引数(ret)は含めません。

 

上記の指定は次の勧告(advice)に対応します。

 

リスト B.3-1 戻り値を受け取るafter returning勧告(advice)メソッド

    public void afterReturning(JoinPoint jp,int ret,String[] theNames){
    ...
    }

 

B.4 例外

結合点(join point)の型がafterThrowingの場合は、例外を勧告(advice)の引数として取得できます。

指定の仕方は次の通りです。要素名returningで指定します。切込み点(pointcut)を指定するargsには戻り値に対応する変数名は書きません。

 

リスト B.4-1 例外を受け取るafter throwing勧告(advice)の定義

 

    <aop:aspect id="advisorThrowing0" ref="adviceThrowing">
        <aop:after-throwing
            pointcut="execution( * msh.pub.springaop.simplecmn.IService.calc*(..)) and args(theNames)"
            method="afterThrowing1"
            throwing="ex"
            arg-names="jp,ex,theNames"/>
    </aop:aspect>

 

aop:beforeの属性pointcutのargsで指定する変数(引数)には、例外に対応する引数(ex)は含めません。

上記の指定は次の勧告(advice)に対応します。

 

リスト B.4-1 例外を受け取るafter throwing勧告(advice)メソッド

    public void afterThrowing1(JoinPoint jp, MyException1 ex, String[] theNames) {
    ...
    }

 

 

付録 C. バグ情報

C.1 adviceのメッソドに引数を設定するとエラーになる

 

バグ情報

http://forum.springframework.org/showthread.php?t=31244

Caused by: java.lang.NoClassDefFoundError:org/objectweb/asm/FieldVisitor

 

Hibernateで使っているものと置き換える

  asm.jar

asm-attrs.jar

 

代わりに

asm-2.2.3.jar

asm-commons-2.2.3.jar

asm-util-2.2.3.jar

 

hibernateへは悪影響があるかも知れない。

(別のテストによればhibernateと両立しない)

注:現時点(2007年7月)ではSpring2.0の機能はhibernateと組み合わせると不安定なようです。

 

 

C.2 xsd-AOPでは同じ名前の勧告(advice)が定義できない

バグ(上記のlibを変更してもこれは直らない)

xmlでafter-throwingの切込み点(pointcut)の設定をすると次の問題あり。(aop:beforeも同じ)

adoviceメソッドはsignatureが異なっていても、メソッド名が一致していると配置できない(実行時にエラーになる)

(( 引数の数が違っていればメソッド名が同じでいいかも知れない---引数の数が違っていてもNGであることをaop:beforeの場合に確認))

メッセージ warning no match for this type name: <引数名>

annotationを使って設定した場合は、signatureが異なっていればメソッド名は同じでもかまわないのでxsd-AOPのバグと思います。

 

付録 D. 例題一覧

名 称 概 要
desc AOPの説明で使った例
intro 導入の説明に使った
intro_xsd xsd-AOP
intro_an @Aspect-AOP
simple 例題1 dtd-AOPの例 6章
simple_1aspect 例題1の補足。勧告を1クラスにまとめた。
simple_xsd 例題2。例題1と同じ。 7章
simple_xsd_1aspect 例題2の補足。勧告を1クラスにまとめた。
simple_an 例題3。例題1と同じ。 8章
10 simple_an_1aspect 例題3の補足。勧告を1クラスにまとめた。
11 simple_an_arg 例題4.引数付き勧告 @Aspect-AOP 9章
12 simple_xsd_arg 例題5.引数付き勧告 xsd-AOP 10章

 

以上