[[Open棟梁Project>http://opentouryo.osscons.jp/]] - [[マイクロソフト系技術情報 Wiki>http://techinfoofmicrosofttech.osscons.jp/]] * 目次 [#b8f54ee6] #contents *概要 [#zfca130b] .NET FrameworkでAOP:Aspect Oriented Programmingを実現するための手法の1つである~ 「透過プロキシ(RealProxyクラス)」の概要、使用方法、そして、サンプル コードについて説明する。 AOPの解説の多くは、下記、本”概要”のような思想を語っていることが多く、~ これを実現するための技術、開発基盤も様々であることから、解説が具体的でないことが多い。 これに対して、ココでは、.NET FrameworkでAOPを実現するための~ 手法の1つである透過プロキシにフォーカスしてこれを説明する。 **技術 [#r06f8e62] AOPを実現する技術(方式)には下記のものがある。 -コンパイル時の静的なウィービング -実行時の動的なウィービング **開発基盤 [#e35b4203] 代表的なAOP開発基盤(古いかも) ***言語個別 [#s0700385] -AspectJ(Java用):http://www.eclipse.org/aspectj/ -AspectC++(C/C++用):http://www.aspectc.org/ -AspectR(Ruby用):http://raa.ruby-lang.org/project/aspectr/ ***フレームワーク個別 [#l05d9f59] -JBossAOP(Java用):http://www.jboss.org/jbossaop -Spring Framework(Java用):http://www.springsource.org/ -Seasar2(Java用):http://s2container.seasar.org/2.4/ja/ ***その他 [#ie6bd523] -AspectWerkz(Jonas Boner等によって開発):http://aspectwerkz.codehaus.org/ *アスペクト指向プログラミングとは? [#g5f8f8ca] アスペクト指向プログラミング(以下、AOP と略す)とは、 オブジェクト指向ではうまく分離できない問題 >各メソッド内に別のメソッドを呼び出す「処理の共通的パターン」がある場合、~ オブジェクト指向ではこれを共通化できず、処理が散在してしまう。 を「アスペクト」と呼び、 アスペクト記述言語(若しくはこれを実現するための各種技術)を用いて、~ 「アスペクト」を別のメソッド(モジュール)に分離して記述することで、~ プログラムに柔軟性を持たせようとする試み。 アスペクトの代表的な利用例としては「ロギング処理」がある。 **ロギング処理(アスペクトの代表的な利用例) [#nd2b6186] #ref(AOP-Logging.png,left,nowrap,ロギング処理(アスペクトの代表的な利用例)) **アスペクトの例 [#qa350377] 以下は、AOPを使用してロギング処理の「アスペクト」を~ 別のモジュール(or 定義)に分離して実装した例である。 #ref(ExampleOfAOP.png,left,nowrap,AOPでのロギング処理の実装例) 実際にコードが実行される際には、ルールに従って指定されたパターン の~ 「アスペクト」が織り込まれる(これをアスペクトのウィービングと呼ぶ)。 **ウィービングの指定方法 [#x5cb142b] ウィービングの指定方法には、 -定義ファイル -アノテーション(メソッド / クラス属性) -クラスのメソッド構成 などがある。 前述の説明に合わせると、透過プロキシを使用したAOPでは、 -アスペクトを透過プロキシ(後述)上に実装する。 -ウィービングされるアスペクトは、使用する透過プロキシにより決定される。 となる。 *透過プロキシ [#ad4aba32] **透過プロキシの概要 [#gbc46098] 透過プロキシとは、種々のリモート処理 基盤技術を使用して境界を越えオブジェクトを転送するためのプロキシである。 -ココでは、AOPを実現するための手法の1つとして透過プロキシを活用する。 -また、RealProxyクラスは透過プロキシを開発するための開発基盤である。 捕捉: -転送されるオブジェクトは、MarshalByRefObject クラスを継承する必要がある。 --MSDN > .NET Frameworkクラス ライブラリ > MarshalByRefObjectクラス~ http://msdn.microsoft.com/ja-jp/library/system.marshalbyrefobject.aspx >MarshalByRefObject の「Marshal」は、一般的には「鉄道 操車場」(分岐器、ハンプなどを経て目的の仕分線に送る設備。ヤードとも呼ばれる)を意味するが、IT用語としては、COMなどで実装されていた「マーシャリング」を指し、アプリケーション ドメイン、プロセス、開発技術などの各境界を越えて、オブジェクトを転送する技術の総称である。開発技術の境界を越えるための技術としては、アンマネージDLLマーシャリングなどがあり、.NETオブジェクト引数をWin32 DLLで使用可能なデータ型の引数に変換(またはその逆を)する。なお、ココでは、MarshalByRefObject(マーシャリング)技術の詳細については触れない。 -RealProxyクラスはabstract基本クラスであり、透過プロキシを開発する際は、このRealProxyクラスを継承する必要がある。 --MSDN > .NET Frameworkクラス ライブラリ > RealProxyクラス~ http://msdn.microsoft.com/ja-jp/library/system.runtime.remoting.proxies.realproxy.aspx **透過プロキシの基本的な動作 [#y244fdd9] 以下、透過プロキシの基本的な動作を説明する。 ***(1) [#pa40385b] -クライアントは、使用したいオブジェクト(以降、オブジェクトAと称す)の情報を透過プロキシに通知し、 -透過プロキシは、リモート処理 基盤技術を使用してオブジェクトAを生成する。 --オブジェクトを生成する方法として、下記の方法が考えられる。 ---コンストラクタから、オブジェクト・インスタンスを直接受け取る方法 ---コンストラクタから、クラス情報を受け取り、~ これを使用してオブジェクト・インスタンスを生成する方法 ---コンストラクタから、リテラルを受け取り、定義情報から対応するクラス情報を取得、~ これを使用してオブジェクト・インスタンスを生成する方法 -クライアントはオブジェクトAの参照の変わりに透過プロキシの参照を得る。~ この透過プロキシの参照は、オブジェクトAの型に設定することができるため、~ クライアントは、あたかもオブジェクトAを直接操作できるかのように錯覚する。 #ref(BasicOperationOfRealProxy1.png,left,nowrap,透過プロキシの基本的な動作1) ***(2) [#j139262c] -透過プロキシの参照は、オブジェクトAの型に設定されているので、~ この型を使用してオブジェクトAに対する呼び出しが可能である。 -このオブジェクトAのクラス型を使用したオブジェクトAへの呼び出しは(実際は)、~ 透過プロキシを使用して呼び出しているので、始めに透過プロキシの~ Invoke メソッドにより呼び出しがフックされる。 --透過プロキシでは、継承したRealProxyのInvokeメソッドをオーバーライドする必要がある。 #ref(BasicOperationOfRealProxy2.png,left,nowrap,透過プロキシの基本的な動作2) ***(3) [#w7016341] -透過プロキシは、Invokeメソッド内でリモート処理 基盤技術を使用し、呼び出しを実際のオブジェクトに転送する。 --透過プロキシのポイントは、透過プロキシのInvokeメソッドには、~ 呼び出しメソッドのメソッド シグネチャの情報が引数として与えられている点である。 ---メソッド シグネチャとは、一般的に、~ 「メソッド名」、「パラメタ数と順序、パラメタの型」、「戻り値の型」などを意味する。 -透過プロキシのInvokeメソッドから使用したいオブジェクトに呼び出しを転送する場合、~ 最終的にレイトバインド 技術を使用することになる。 --.NETでは、System.Reflection名前空間のクラスライブラリを使用してレイトバインドを実現する。 -レイトバインドを使用する場合は、メソッド シグネチャを固定する方式が主流であるが、 --言語によっても異なるが、C++、C#のような「強い静的型付け」という言語的特性を持つ言語に於いては、~ 実行に任意のオブジェクトに対して任意のメソッド呼び出しを試みるという処理が許可されていないため。 ---C++では、これを仮想関数呼び出しやCOMで実現する。 ---C#では、System.Reflection名前空間のクラスライブラリを使用して実現する。 ---これに対し、VBではobject型に任意のメソッド呼び出しを記述できる -透過プロキシのInvokeメソッドには、~ 呼び出しメソッドのメソッド シグネチャの情報が引数として与えられるため、~ これを使用して、どのようなメソッド呼び出しも転送することができる。 #ref(BasicOperationOfRealProxy3.png,left,nowrap,透過プロキシの基本的な動作3) **透過プロキシでAOPを実現 [#ef5ee71f] 透過プロキシの働きについては、[[前項>#y244fdd9]]で説明した通りである。 -ここまでの説明で明らかであるが、透過プロキシを使用したAOPの実現方法とは、~ 透過プロキシのInvoke メソッドにアスペクトを実装するだけの簡単なものである。 --ここでは、透過プロキシの本来の~ 「リモート処理 基盤技術を使用して境界を越えオブジェクトを転送する」~ 役割は不要であるため、その部分の実装も不要である。 -また、様々な仕掛け(定義情報など)を用いることで、~ 以下の様な機能を追加することも可能である。 --以下からウィービングされるアスペクトのパターンを可変にする。 ---コンストラクタに指定されたリテラル ---メソッドに指定されたアノテーション(クラス / メソッド属性) --アスペクトの実装位置を、透過プロキシ上から他のモジュール上へ移動し、呼び出す処理を選択する。 ---コンストラクタに指定されたリテラル ---メソッドに指定されたアノテーション(クラス / メソッド属性) しかし、ココでは、これらの応用的な利用方法までは説明しない。 *動作確認用のサンプル プログラム [#lbff0d38] 透過プロキシを使用したAOPの動作確認用サンプル プログラムを用いて、AOPの実装方法を説明する。 -サンプル プログラムはConsoleアプリケーションで作成されている。 -プロジェクトの構成は次のようになっている。 クラス(モジュール)一覧 |項番|クラス(モジュール)|説明|h |1|TG.cs|ターゲット クラス(ココで言うオブジェクトA| |2|PR1.cs|透過プロキシ1(オブジェクトAのインスタンスを渡すバージョン| |3|PR2.cs|透過プロキシ2(オブジェクトAのクラス型情報を渡すバージョン| |4|MyInt.cs|参照型を引数に使用した場合の動作検証に用いるクラス| |5|Program.cs|各種テスト ケースを実行するエントリポイント メソッド| **ターゲット クラス [#k301427f] ターゲット クラスは、本書で説明した「オブジェクトA」、~ すなわち、クライアントが使用したいオブジェクトのことである。 実装のポイントは、MarshalByRefObjectクラスを継承することであり、その他は任意の実装が可能である。~ サンプル プログラムでは、様々なシグネチャのメソッドの呼び出しをテストするために、~ 様々なシグネチャのメソッドをターゲット クラスに実装してある。 **透過プロキシ [#r556d2a8] -実装のポイントは、RealProxyクラスを継承することである。 -サンプル プログラムには、アスペクトは実装していないので、~ 任意のアスペクトのパターンを、ここでウィービングするように実装する。 ***コンストラクタ [#kc1ae4e5] また、透過プロキシ1、2で、コンストラクタの実装が若干異なる。~ 異なる点は、ターゲット クラスのオブジェクト インスタンスを生成する方法である。 -透過プロキシ1~ コンストラクタから、ターゲット クラスのオブジェクト インスタンスを直接受け取る。 -透過プロキシ2~ コンストラクタからクラス情報を受け取り、~ これを使用してターゲット クラスのオブジェクト インスタンスを生成する。 ***Invokeメソッド [#cf5bea3e] 透過プロキシ1、2ともInvokeメソッドの実装は同じである。