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)の分離
分離前のモジュールは二つのモジュールに分割されます。実際の分割はもっと複雑かもしれません。論理的には基礎関心事(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)の分割(シーケンス図)
灰色の箱は交錯関心事(cross-cutting concern)を指します。分離後は、交錯関心事(cross-cutting concern)が基礎関心事(base concern)と分離され、基礎関心事(base-concern)から呼び出される形になります。
例を示します。
リスト2.1-1はAOPを使わないで記述したトランザクションを含むDBへのレコード追加の処理です。
これを分割してできた基礎関心事処理と交錯関心事処理がそれぞれリスト2.1-2とリスト2.1-3です。
リスト2.1-1 分割前
|
注:赤字の部分が交錯関心事(cross-cutting concern)のための処理
リスト2.1-2 基礎関心事処理
|
リスト2.1-3 交錯関心事処理
|
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 編込み後の処理
|
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)の型
# | 型 | 説 明 |
1 | before | 呼び出したメソッドの処理開始直前 |
2 | after returnig | メソッドから戻る直前(呼び出されたメソッドが例外を投げないでリターンした直後) |
3 | around | 上記の双方 |
4 | after throwing | 呼び出し先で例外が生じて呼び出し元にもどる時点 |
5 | 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の実装
基礎関心事処理の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 例題のクラス図
図3.1-1と図3.2-1の対応は表3.2-1の通りです。
表3.2-1 Spring1.2 AOP 実装と例の対応
# | Spring1.2 AOP 実装 | 例 |
1 | 基礎関心事処理クラス | Service |
2 | 基礎関心事処理interface | IService |
3 | Advisor | RegexpMethodPointcutAdvisor(3インスタンス)(*1) |
4 | Adviceクラス | AdviceBefore, AdviceAfter, AdviceThrowing |
5 | 基礎関心事処理クラスを利用するクラス | Client |
注 *1:図3.2-1の中ではクラス名をAdvisorと記しています。
この構成をSpringのアプリケーションコンテキストの中に記述します。この設定を少しずつ見ていきます。
3.2.1 基礎関心事処理クラス
リスト3.2-1基礎関心事処理クラス
|
AOPを使わない場合と変わらない設定です。
3.2.2 勧告(advice)
リスト3.2-2 勧告(advice)クラス
|
AdviceはAOPに関係していますが、その設定は通常のクラスと変わりません。
3個の勧告の例ですが数に制限はありません。
3.2.3 Advisor
リスト3.2-3 勧告(advice)クラス
|
AdvisorのクラスはSpringが提供するクラスです。このクラスに二つのプロパティを設定しています。図3.2-1に示すように勧告(advice)と切込み点(pointcut)を設定するプロパティです。
プロパティadviceにはadviceBeforeを指定しています。
プロパティpatternsには切込み点(pointcut)として使う正規表現を指定します。複数組の正規表現を設定できます。ここでは、「.*」を指定していますので、すべてのメソッドが対象になります(正規表現 .*はすべての文字列に合致します)。
上記の例はadviceBeforeに対応するAdvisorです。他の二つの勧告(advice)についても同様にAdvisorを設定できます。説明は略します。
3.2.4 proxy
リスト3.2-4 proxyクラス
|
proxyのクラスはSpringが提供するクラスです。このクラスに次の3個のプロパティを設定します。
(1) 基礎関心事処理クラスが実装しているinterface
(2) 基礎関心事処理クラス(のインスタンス)
(3) Advisor
Advisorは複数個指定できます。
3.2.5 基礎関心事処理を使うクラス
リスト3.2-5 基礎関心事処理を使うクラス
|
基礎関心事処理を使うクラスはAOPを使わない通常のクラスですから、その指定も通常通りです。ただ、Serviceの参照を設定するプロパティに設定するのはServiceの参照ではなくproxyです。
3.3 導入(introduction)機能
これまで説明したAOPは、特定の結合点(join points)で処理を横取りして、勧告(advice)に制御を渡す手法を使っています。
AOPにはこれとは別に導入(introduction)の手法があります。特定のクラスにフィールドやメソッドを埋め込みます。ただし、Springではメソッドを埋め込むだけです。
図3.3-1は導入(introduction)の概要を説明する図です。
図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)の実装
結合点群に関する実装との違いは次の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)の例題のクラス図
これは、図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)されたメソッドの呼び出し
|
serviceを導入するinterfaceでキャストして使います。serviceは見かけはターゲットのクラスです。実際にはproxyです。
3.4.2 基礎関心事処理クラスのメソッド呼び出しのインターセプト
Serviceクラスのメッソドの呼び出しをインターセプトして呼び出されるinvokeメソッドの中で、lockedがtrueのときはsetXxxxメソッドの実際の処理をスキップするコードをリスト3.4-2に示します。
リスト3.4-2基礎関心事処理クラスのメソッド呼び出しのインターセプト
|
アプリケーションコンテキストの設定方法を見ていきます。
3.4.3 基礎関心事処理クラス
リスト3.4-3基礎関心事処理クラス
|
AOPを使わない場合と変わらない設定です。
3.4.4 勧告(advice)
リスト3.4-4 勧告(advice)クラス
|
3.4.5 Advisor
リスト3.4-5 Advisor
|
設定の方法は通常のクラスと同じです。ただし、引数付きのコンストラクタを使っています。第2引数は、Spring beanのidではなく、タイプがClassの定数を設定しています。これらの機能はAOP専用のものでなくSpringを使うときにどこでも使える機能です(資料3)。
3.4.6 proxy
リスト3.4-6 proxyクラス
|
proxyのクラスはSpringが提供するクラスです。このクラスに次の3個のプロパティを設定します。
(1) 基礎関心事処理クラスが実装しているinterface
(2) 基礎関心事処理クラス(のインスタンス)
(3) Advisor
3.4.7 基礎関心事処理を使うクラス
リスト3.4-7 基礎機能を使うクラス
|
基礎関心事処理を使うクラスは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に関する設定はaop:config要素の中で行います。
様相(aspect)は、この中で、aop:aspect要素で行います。idと様相(aspect)を実装したbeanのidをここで指定します。様相(aspect)を実装したbeanは通常のbeanと同じ方法で指定します。
4.1.2 切込み点(pointcut)
リスト4.1-2 切込み点(pointcut)の定義
|
切込み点(pointcut)は、aop:config中で、aop:aspect要素で行います。指定できる情報はidとexpressionです。詳細は資料2を参照下さい。
切込み点(pointcut)を指定する場合は、aop:aspectより前に指定しなければなりません。
4.1.3 勧告(advice)
(1)before
リスト4.1-3 before勧告(advice)の定義
|
勧告(advice)は様相(aspect)の定義の中で設定します。一つの様相(aspect)に複数の勧告(advice)を指定できます。これらの勧告(advice)は同じ型の勧告(advice)である必要はありません。
使う切込み点(pointcut)はaop:cofigのレベルで定義された切込み点(pointcut)をidで参照する方法と直接定義を書く方法があります。①は参照する方法、②は直接定義する方法を使っています。
切込み点(pointcut)を指定するこの二つの方法は他の型の勧告についても同じです。この節の残りの例には参照する方法だけ使います。
(2)after returning
リスト4.1-4 after returning勧告(advice)の定義
|
指定の方法はbefore型と同じです。ただし、戻り値を取り出す設定ができます。①は戻り値を取り出す設定はしていません。②は戻り値を取り出す設定(returning属性)をしています。引数の名前を指定します。この場合、doSomethingAfterReturning2の引数は次の形になります。
リスト4.1-5 戻り値を受け取るためのafter returning勧告(advice)メソッドのシグネチャ
|
(3)around
リスト4.1-6 around勧告(advice)の定義
|
指定の方法はbefore勧告(advice)と同じです。aroudはbeforeとafter-returningを兼ねています。before又はafter-returningで済む場合は、aroundを使わないでそちらを使うべきです。
around勧告(advice)のメソッドは次の形です。第1引数は必ずProceedingJoinPointです
リスト4.1-7 before勧告(advice)メソッドのシグネチャとターゲットのメソッドの呼び出し
|
(4)after throwing
リスト4.1-8 after throwing勧告(advice)の定義
|
指定の方法はbefore型と同じです。ただし、投げられた例外オブジェクトを勧告の引数として取り出す設定ができます。①は例外オブジェクトを受け取る設定はしていません。②は例外オブジェクトを受け取る設定(throwing属性)をしています。引数の名前を指定します。この場合、doSomethingThrowing2の引数は次の形になります。
リスト4.1-8 after throwing勧告(advice)メソッドのシグネチャ
|
(5)after(finally)
リスト4.1-8 after勧告(advice)の定義
|
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を使った場合)
|
(2)dtdを使った指定
リスト4.2-2 @Aspectを有効にする設定(dtdを使った場合)
|
4.2.1様相(aspect)
リスト4.2-3 様相(aspect)のbeanの定義
|
通常のクラスと同じ方法で様相(aspect)を実装するクラスをbeanとして設定します。
様相(aspect)を実装するクラスは次の形で作成します。
リスト4.2-4 @Aspectアノテーション
|
class文のアノテーションとして@Aspectを指定します。
通常のクラスと同じようにフィールドとメソッドを記述できます。また、この後で説明する切込み点(pointcut)や勧告(advice)、導入(introduction)をアノテーションを付けて記述できます。
4.2.2 切込み点(pointcut)
リスト4.2-5 切込み点(pointcut)アノテーション
|
切込み点(pointcut)の定義は、対象のmethodのシグネチャーと比較する部分と、切込み点(pointcut)のシグネチャー(切込み点(pointcut)の名前又はIDとして使われる)からなります。シグネチャーの戻り値はvoidでなければなりません。
対象のmethodのシグネチャーと比較する部分の規則は付録 Aを参照下さい。
例の場合、切込み点(pointcut)の名前はpointcut1です。someMethodという名前のメソッドに合致します。
4.2.3 勧告(advice)
(1)before
リスト4.2-6 before勧告(advice)アノテーション
|
勧告(advice)は様相(aspect)の定義の中で設定します。一つの様相(aspect)に複数の勧告(advice)を指定できます。これらの勧告は同じ型の勧告である必要はありません。
使う切込み点(pointcut)は@Beforeアノテーションで指定します。定義済みの切込み点(pointcut)をidで参照する方法と直接定義を書く方法があります。①は参照する方法、②は直接定義する方法を使っています。
切込み点(pointcut)を指定するこの二つの方法は他の型の勧告についても同じです。他の説明では参照する方法だけを例示します。
(2)after returning
リスト4.2-7 after returning勧告(advice)アノテーション
|
指定の方法はbefore勧告(advice)と同じです。ただし、戻り値を取り出す設定ができます。①は戻り値を取り出す設定はしていません。②は戻り値を取り出す設定(returning属性)をしています。引数の名前を指定します。returningで指定した値(rtn)は、このアノテーションが付いているメソッド(doSomethingAfterReturning2)の引数の名前に似ています。
(3)around
リスト4.2-8 around勧告(advice)アノテーション
|
指定の方法はbefore勧告(advice)と同じです。aroudはbeforeとafter-returningを兼ねています。before又はafter-returningで済む場合は、aroundを使わないでそちらを使うべきです。
around勧告(advice)の第1引数はProceedingJoinPointです。
(4)after throwing
リスト4.2-9 after throwing勧告(advice)アノテーション
|
指定の方法はbefore勧告(advice)と同じです。ただし、投げられた例外オブジェクトを勧告の引数として取り出す設定ができます。①は例外オブジェクトを取り出す設定はしていません。②は例外オブジェクトを取り出す設定(throwing属性)をしています。引数の名前を指定します。この場合、doSomethingThrowing2の引数は次の形になります。引数で指定した例外のクラスに一致した例外が投げられた場合に該当する勧告(advice)に制御が移ります。
(5)after(finally)
リスト4.2-10 after勧告(advice)アノテーション
|
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)の設定
|
これはAspectJアノテーションを使って設定することも出来ます。これをリスト4.3-2にこれを示します。
リスト4.3-2 @Aspectを使った導入(introduction)の設定
|
例題はソース中の次のパッケージを参照下さい。
(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 例に使うメソッドのシグネチャ
|
引数namesを勧告(advice)に渡す方法は次の通りです。
リスト5.1-2 引数を受け取るための@Beforeアノテーション
|
引数がない場合の設定と違う点は && arg(theNnames)があることです。
&&は論理演算子です。Javaの&&と同じ意味です。
argsは次の意味を持ちます。
(1)引数が1個
(2)その引数の型がString[]
(3)勧告(advice)に該当のメソッド(例ではcalcTotal)の引数が渡される。
切込み点(pointcut)があらかじめ定義されている場合の設定は次の通りです。
リスト5.1-3 切込み点(pointcut)アノテーションを参照する@Beforeアノテーション
|
@Pointcutアノテーションで指定する内容は、@Beforeで指定する内容と同じです。argsも必要です。切込み点の名前を意味するメソッドの引数は、argsの指定に対応して指定します。
@Beforeアノテーションはこの@Pointcutアノテーションを参照する形で指定します。@Pointcutの名称は引数を含めて指定します。ただし、引数の型は要りません。
例として@Beforeの場合を挙げましたが、他の結合点(joint points)の型でも同じ方法で指定できます。
ただし、@Aroundの場合は、第1引数は必須です。これは切込み点(pointcut)の中にargsで指定しなくても渡されます。逆にいうと、勧告(advice)の仮引数として必須です。これを次に示します。
リスト5.1-4 aroud勧告(advice)の第1引数
|
第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アノテーション
|
内容は引数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アノテーション
|
after型の勧告(advice)の場合は戻り値を取得できません。
around型の場合も戻り値を引数として取得できません。しかし、勧告(advice)の中でメソッドを呼び出しますので、戻り値を勧告(advice)の中で使えます。
5.4 例外
結合点(join point)の型がafterThrowingの場合は、例外を勧告(advice)の引数として取得できます。
指定に仕方は次の通りです。要素名throwingで指定します。切込み点(pointcut)を指定するargsには戻り値に対応する変数名は書きません。
リスト5.4-1 例外を受け取る@AferThrowing
|
after型の勧告(advice)は例外を取得できません。
5.5 引数の対応
メソッドの引数と勧告(advice)の引数の対応付けは正常にできるとは限りません。通常、ソース上の変数名はを実行時に残っていません。残すにはそのための手当てが必要ですがそれについてはこの解説では述べません。引数を勧告(advice)に渡す場合は全引数を順序を変えずに渡す原則で臨むことです。たとえ、勧告(advice)に全引数が必要でなくともです。
5.6 引数の加工
結合点(join point)の型がaround型の場合は、メソッドに渡す引数を勧告(advice)内で生成又は加工できます。順序と型が一致していなければなりません。
例を次に示します。
リスト5.6-1 引数を加工するaround勧告(advice)
|
この例題では、勧告(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 クラス図
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 処理のシーケンス
AOPのテストは、Serviceを基礎関心事処理(ターゲット)として行います。そのクラス構成を図6.1-3に示します。
図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基礎関心事処理クラス
|
AOPを使わない場合と変わらない設定です。ServiceとDaoの設定も同じように出来ますので省略しています。
注意する点は、serviceプロパティにproxy(のid)を設定していましたが、xsd-AOPを使う場合は、基礎関心事処理クラスのbean(のid)をここに設定します。
7.1.2 勧告(advice)
リスト7.1-2 勧告(advice)クラス
|
勧告(advice)クラスはAOPに関係していますが、その設定は通常のクラスと変わりません。この点はdtd-AOPと変わりません。
ただし、具体的なクラスの内容は違っています。dtd-AOPの場合は、Springが提供するinterfaceを実装しなければなりませんでした。その結果、メソッド名は固定されていました。
xsd-AOPでは、自由にメソッド名は決められます。しかし、この例題はdtd-AOPと比較しやすくできるようにするため、メソッド名を同じにしています。
7.1.3 Advisor(Aspect)
リスト7.1-3 Advisor(Aspect)
|
この例題では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クラス
|
7.1.5 基礎関心事処理を使うクラス
リスト2.3-5 基礎機能を使うクラス
|
基礎関心事処理を使うクラスは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)
|
このリストの注目する点は次の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)の定義
|
これは切込み点(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アノテーション
|
①は勧告されるメソッドの引数を受け取る指定です。
③はそれに加えて結合点(join point)の情報も受け取ります。
②は③で使う結合点(pointcut)を定義するアノテーションです。
9.2.2 after returning
リスト9.2-2 @AfterReturningアノテーション
|
結合点(join point)と戻り値と勧告されるメソッドの引数を受け取ります。
9.2.3 around
リスト9.2-3 @Aroundアノテーション
|
①は勧告されるメソッドの引数を受け取ります。第1引数(pjp)は必須です。
②は受け取った引数を変更して、勧告(advice)を受けるメソッドを呼び出す例です。
③と④で引数に加工を加えます。
⑤は加工後の値をObject配列の要素として目的のメソッドを呼び出しています。
9.2.4 after throwing
リスト9.2-4 @AfterThrowingアノテーション
|
結合点の情報と例外と勧告されるメソッドの引数を受け取ります。
9.2.5 after
リスト9.2-5 @Afterアノテーション
|
勧告されるメソッドの引数を受け取ります。
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)の定義
|
①は勧告されるメソッドの引数を受け取る指定です。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)の定義
|
結合点(join point)と戻りと勧告されるメソッドの引数を受け取ります。戻り値の変数名はreturning属性で指定します。arg-namesでは結合点(join point)(jp)情報と戻り値(ret)も指定します。pointcut属性のargsにはこれらは指定しません。
10.2.3 around
リスト10.2-3 around勧告(advice)の定義
|
①は結合点(join point)の情報と勧告されるメソッドの引数を受け取ります。第1引数(pjp)は必須です。pjp以外に引数がない場合は、arg-namesを省略できます。
②は二つの引数を受け取ります。この二つの引数を加工した後、勧告(advice)を受けるメソッドに渡す例ですが、この指定にはその処理に関係した設定は現れません。
10.2.4 after throwing
リスト10.2-4 after throwing勧告(advice)の定義
|
結合点の情報と例外と勧告されるメソッドの引数を受け取ります。
10.2.5 after
リスト10.2-5 after勧告(advice)の定義
|
結合点の情報と勧告されるメソッドの引数を受け取ります。
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)の定義
|
次の勧告(advice)に対応します。
リスト B.1-2 引数を受け取るbefore勧告(advice)メソッド
|
これはaop:beforeの例ですが、他の結合点(join point)についても同じです。@Aspect異なる点は、&&がandに変わり、アノテーションの要素名argNamesがarg-names属性になることです。
aroundの場合は、第1引数が固定されています。例を次に示します。
リスト B.1-3 引数を受け取るaround勧告(advice)の定義
|
第1引数はargsには指定せず、arg-namesには指定します。pjp以外に引数がない場合はarg-namesは要りません。
B.2 結合点(join point)情報
リスト B.2-1 結合点(join point)情報を受け取るbefore勧告(advice)の定義
|
aop:beforeの属性pointcutのargsで指定する変数(引数)には、結合点(join point)の情報に対応する引数(jp)は含めません。pointcut属性で指定するのは勧告(advice)されるメソッドに関する情報です。これにはjpは含まないからです。before以外の他の型の結合点(join points)でもargsの指定の仕方は同じです。
上記の指定は次の勧告(advice)に対応します。
リスト B.2-2 結合点(join point)情報を受け取るbefore勧告(advice)メソッド
|
B.3 戻り値
リスト B.3-1 戻り値を受け取るafter returning勧告(advice)の定義
|
aop:beforeの属性pointcutのargsで指定する変数(引数)には、戻り値に対応する引数(ret)は含めません。
上記の指定は次の勧告(advice)に対応します。
リスト B.3-1 戻り値を受け取るafter returning勧告(advice)メソッド
|
B.4 例外
結合点(join point)の型がafterThrowingの場合は、例外を勧告(advice)の引数として取得できます。
指定の仕方は次の通りです。要素名returningで指定します。切込み点(pointcut)を指定するargsには戻り値に対応する変数名は書きません。
リスト B.4-1 例外を受け取るafter throwing勧告(advice)の定義
|
aop:beforeの属性pointcutのargsで指定する変数(引数)には、例外に対応する引数(ex)は含めません。
上記の指定は次の勧告(advice)に対応します。
リスト B.4-1 例外を受け取るafter throwing勧告(advice)メソッド
|
付録 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. 例題一覧
# | 名 称 | 概 要 |
1 | desc | AOPの説明で使った例 |
2 | intro | 導入の説明に使った |
3 | intro_xsd | xsd-AOP |
4 | intro_an | @Aspect-AOP |
5 | simple | 例題1 dtd-AOPの例 6章 |
6 | simple_1aspect | 例題1の補足。勧告を1クラスにまとめた。 |
7 | simple_xsd | 例題2。例題1と同じ。 7章 |
8 | simple_xsd_1aspect | 例題2の補足。勧告を1クラスにまとめた。 |
9 | 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章 |
以上