S2Container.NETのdiconファイルを拡張
次に、もう少し実践的な例を説明していきます。
AOPによる「既存の振る舞いへの作用」という側面を、S2Container.NETにおけるdiconファイルの処理を拡張する事で実施してみます。
具体的に、AspectDNGを使ってdiconファイルのargやproperty要素にJScript.NET式以外の、より表現方法が多彩な式言語を利用できるようにします。
必要な環境
このサンプルの実行に必要になる環境は次のとおりです。
- S2Container.NET 1.2.0
- Spring.NET 1.0.2(Expression Evaluation機能だけ使用)
アスペクトの定義クラス作成
それでは、diconファイルのargやproperty要素の処理を拡張するためのアスペクト定義クラスを作成していきます。
技術概要
まず、S2Container.NETのdiconファイル処理の実装に関して、簡単に説明します。
diconファイルのarg要素の処理は、Seasar.Framework.Container.Impl.ArgDefImplクラスのValueプロパティの取得(get)で実装されています。
また、ArgDefImplクラスは、property要素を処理するPropertyDefImplクラスや、aspect要素を処理するAspectDefImplクラスの親クラスでもあるため、ArgDefImplのValueプロパティの取得にアスペクトをウィービングすることで、arg、property、aspect要素の既存の振る舞いも変更することが可能になります。
ところで、今回は、JScript.NET式の代わりにSpring.NETのSpring.Expressions.ExpressionEvaluatorクラスで処理できる式を使うことになりますが、アスペクト定義クラスの実装を簡単にするため#キー名という式だけExpressionEvaluatorで処理し、その他は元のArgDefImplの実装で処理することにします。
Spring.Expressions.ExpressionEvaluatorクラスには式を評価するGetValueクラスメソッドが用意されており、このメソッドの第3引数で渡すIDictionaryオブジェクトの要素を、第2引数の文字列内で#キー名という記述で参照します。アスペクトクラスExpressionExtendAspectの作成
それでは、アスペクトの定義クラスを作成していきます。
ArgDefImplクラスのValueプロパティ取得の実行部分をジョインポイントにし、Expressionプロパティの値が#を含む場合にExpressionEvaluator.GetValueを実行、それ以外は元の処理を実施するアスペクトを定義します。
また、ExpressionEvaluatorの式で参照するオブジェクトに、コンテナ(IS2Containerの実装オブジェクト)とアプリケーション構成ファイルappSettingsの内容を渡しています。
using System; using System.Collections; using System.Configuration; using DotNetGuru.AspectDNG.Joinpoints; using Seasar.Framework.Container; using Spring.Expressions; public class ExpressionExtendAspect { [AroundBody("* *.ArgDefImpl::get_Value()")] public static object ValueProperty(MethodJoinPoint jp) { object result = null; IArgDef adef = jp.RealTarget as IArgDef; string expression = adef.Expression; if (expression != null && expression.IndexOf("#") > -1) { Hashtable table = new Hashtable(); table["container"] = adef.Container; table["appSettings"] = ConfigurationManager.AppSettings; result = ExpressionEvaluator.GetValue(null, expression, table); } else { result = jp.Proceed(); } return result; } }
アスペクトのウィービング
事前準備
サンプルを作成するディレクトリに、下記のアセンブリをコピーしておきます。
- aspectdng.exe
- Seasar.dll(s2container.net-1.2.0-.net2.0\s2container.net\build)
- log4net.dll(s2container.net-1.2.0-.net2.0\s2container.net\lib)
- Spring.Core.dll(Spring.NET\bin\net\2.0\realease)
- antlr.runtime.dll(Spring.NET\bin\net\2.0\realease)
アスペクト定義クラスのビルド
アスペクト定義クラスをビルドしてアスペクト用のアセンブリを作成します。
>csc /out:ExpressionAop.dll /target:library/r:aspectdng.exe;Seasar.dll;Spring.Core.dll ExpressionExtendAspect.cs
Seasar.dllへのウィービング
AspectDNGでは、ウィービング対象のアセンブリとアスペクト定義クラスのアセンブリを別ファイルで用意することができ、既存アセンブリへのウィービングが可能です(ただし、厳密な名前を保持できないなどの問題があります)。
それでは、既存アセンブリ「Seasar.dll」に、上記で作成した「ExpressionAop.dll」をウィービングしていきます。
既存アセンブリに対して別アセンブリのアスペクトをウィービングするのは、非常に簡単で、「aspectdng.exe」実行時の第1引数にウィービング対象の既存アセンブリ「Seasar.dll」を、第2引数にアスペクトのアセンブリ「ExpressionAop.dll」を指定するだけです。
>aspectdng.exe Seasar.dll ExpressionAop.dll
アスペクトウィービング後の「Seasar.dll」と、元の「Seasar.dll」のバックアップである「Seasar.dll.backup」が作成されます。
アスペクト定義クラスの再ビルド
現時点のAspectDNGの実装では、厳密な名前を付けたアセンブリにアスペクトをウィービングすると厳密な名前が無くなります。
ですが、「ExpressionAop.dll」は、ウィービング前の「Seasar.dll」を使ってビルドしているため、実行時に厳密な名前の付いた「Seasar.dll」を必要とします(ウィービング後の「Seasar.dll」を使うと実行時エラーが発生します)。
この問題を簡単に回避するため、ウィービング後の厳密な名前の無い「Seasar.dll」を使って、「ExpressionAop.dll」を再ビルドする方法をとることにします。
>csc /out:ExpressionAop.dll /target:library/r:aspectdng.exe;Seasar.dll;Spring.Core.dll ExpressionExtendAspect.cs
S2Container.NETを使った実行クラス作成
ここではウィービング後の「Seasar.dll」を検証するために、S2Container.NETを使った実行クラスを作成していきます。
DI対象クラス作成
まず、簡単なDI対象クラスを作成していきます。インターフェイスとしてIData、その実装クラスであるDataImpl、IDataを注入するクラスであるDataProcessorを用意しました。
public interface IData { void Print(); }
using System; public class DataImpl : IData { private string name; private int point; public DataImpl(string name) { this.name = name; } public int Point { set { this.point = value; } } public void Print() { Console.WriteLine("name={0}, point={1}", this.name, this.point); } }
public class DataProcessor { private IData data; public IData Data { set { this.data = value; } } public void Process() { this.data.Print(); } }
実行クラス作成
app.diconのDI定義からDataProcessorを取得し、実行するクラスを作成します。
using System; using Seasar.Framework.Container; using Seasar.Framework.Container.Factory; public class Tester { private const string PATH = "app.dicon"; public static void Main(string[] args) { IS2Container s2c = S2ContainerFactory.Create(PATH); DataProcessor processor = s2c.GetComponent("processor") as DataProcessor; processor.Process(); } }
アプリケーション構成ファイル作成
app.diconで参照するための値、nameとtestModeを定義します。
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <appSettings> <add key="name" value="チェックデータ" /> <add key="testMode" value="false" /> </appSettings> </configuration>
diconファイル作成
「Seasar.dll」へのウィービング効果を検証するため、通常のdiconファイルでは表現できない以下の点を取り入れます。
- アプリケーション構成ファイルの値を加工した値を、コンストラクタの引数に指定する
- アプリケーション構成ファイルの値によって注入するコンポーネントを変更する
containerやappSettingsが使用できるようになっています。 data、testdata、processorの3つのコンポーネント定義を用意し、dataは一般的な記述、testdataはアプリケーション構成ファイル「Tester.exe.config」のnameの値にtestという文字列を繋げた文字列をコンストラクタの引数に指定、processorは、testModeの値がtrueの場合にコンポーネントtestdataを、falseの場合にコンポーネントdataをDataプロパティに注入しています。
<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE components PUBLIC "-//SEASAR2.1//DTD S2Container//EN" "http://www.seasar.org/dtd/components21.dtd"> <components> <component name="data" class="DataImpl"> <arg>"データ1"</arg> <property name="Point">100</property> </component> <component name="testdata" class="DataImpl"> <arg>#appSettings['name'] + 'test'</arg> </component> <component name="processor" class="DataProcessor"> <property name="Data"> #appSettings['testMode']? #container.GetComponent('testdata'): #container.GetComponent('data') </property> </component> </components>
ビルド
以上、作成したクラスをビルドして「Tester.exe」を作成します(ウィービング後の「Seasar.dll」を参照先/rに指定します)。
>csc /r:Seasar.dll Tester.cs *Data*.cs Tester.cs
実行
それでは、ウィービング後の「Seasar.dll」と実行クラスを使って、ウィービングの効果を確認していきます。
testModeの値がfalseの場合
「Tester.exe.config」のtestModeの値をfalseに設定し、「Tester.exe」を実行します。
>Tester.exe name=データ1, point=100
実行結果より、コンポーネントdataが注入されていることを確認できます。
testModeの値がtrueの場合
「Tester.exe.config」のtestModeの値をtrueに設定し、「Tester.exe」を実行します。
>Tester.exe name=チェックデータtest, point=0
実行結果より、コンポーネントtestdataの注入と、testdataのコンストラクタの引数に「Tester.exe.config」のnameの値とtestを繋げた文字列が設定されていることを確認できます。
おわりに
以上、AspectDNGに関して説明してきました。AspectDNGは既存アセンブリへのウィービングや、「aspectdng.exe」を使用するだけという利用の容易さなどの特徴をもち、.NETで本格的なAOPを実施したい方にとって非常に有用なツールです。
今回は説明できませんでしたが、インタータイプ宣言や、GAOP(ウィービング時にアスペクトの内容を決定できる)という機能も備わっており、JavaのAOPツールであるAspectJやJBossAOPに比べても遜色ないツールに仕上がりつつあります。
ドキュメントなど、全体的に情報が少ない印象がありますが、比較的簡単に使えます。これを機にAspectDNGを使い始めてみてはいかがでしょうか。
参考資料
- AspectDNG Documentation
- S2Container.NET ドキュメント JScript.NET式
- Spring.NET Reference Documentation - Chapter 7. Expression Evaluation
- 『AspectJによるアスペクト指向プログラミング入門』 長瀬嘉秀 著、ソフトバンククリエイティブ、2004年4月
- JavaWorld 2006年 1月号 『アスペクト指向開発の AtoZ』
- ITアーキテクト vol.04 『アーキテクチャ設計技術の最新潮流-01アスペクト指向プログラミング』

/r:aspectdng.exe;Seasar.dll;Spring.Core.dll ExpressionExtendAspect.cs