はじめてのアスペクト指向
アスペクト指向については、RealProxyやReflectionを使って'モドキ'はネタにしましたが、真っ当なのは試してみたことがありません。
・Aspect Weaver Tutorial
↑これを見ると簡単そうだったので、ちょっと実験してみることにしました
以下では、AspectWeaverを使用します。まずは、リンク先からAspectWeaver0.6.zipをダウンロードします。(2005/10/16現在)
zipファイルにはソースファイルまるごと入っていますが、今回必要なファイルは3つだけです。
- AspectWeaver\bin\Release\AspectWeaver.exe
- AspectWeaver\AspectWeaver.Aspects\bin\Release\AspectWeaver.Aspects.dll
- AspectWeaver\AspectWeaver.xsd
これらをどこか適当なディレクトリに纏めて放り込み、Pathを通します。私の環境では「c:\home\bin\AspectWeaver」に放り込んでいますので、今後はここにあるものとして話を進めます。
AspectWeaverを使う準備が整いましたので、サンプルの構成について紹介します。先ずは、before、after、aspectsというディレクトリを用意します。
beforeにはCalc.dllというアスペクトを注入するターゲットとなるアセンブリを、aspectsには注入するアスペクトアセンブリを置くことにします。アスペクトは取りあえず、メソッドの呼び出しを記録するだけの簡単なものにしましょう。最後に、afterにはAspectWeaverによってアスペクトが注入されたCalc.dllがコピーされます。
では、ターゲットとなるCalc.dllを用意しましょう。
using System; public class Calc { public int Plus (int x, int y) { return x + y; } public int Minus (int x, int y) { return x - y; } public void Print (int x, int y) { Console.WriteLine ("({0}, {1})", x, y); } }
単純なクラスですね。beforeディレクトリにCalc.csと名前を付けて保存し、コンパイルします。
$ csc /t:library Calc.cs
次に注入するアスペクトを用意しましょう。
using System; using AspectWeaver.Aspects; public class MethodSelfIntroduceAspect : Aspect { public MethodSelfIntroduceAspect() { } // 全てのメソッドに対して注入 [InlineAtStart("//Method")] public void Introduce() { Console.Write(string.Format("Calling method: {0}\n", GetContextInfo())); object[] args = GetArguments(); if (args.Length > 0) { Console.Write(string.Format("Arguments: {0}", args[0])); for (int i = 1; i < args.Length; i++) { Console.Write(string.Format(", {0}", args[i])); } Console.Write("\n"); } } // メソッド名が'P'で始まるものに注入 [InlineAtStart("//Method[starts-with(@name, 'P')]")] public void IntroduceOnlyP () { Console.Write(string.Format("Calling method 'P': {0}\n", GetContextInfo())); } }
コメントからソースが何をやっているかは想像がつくと思います。aspectsディレクトリにMyAspectsと名前を付けて保存し、コンパイル。
$ csc /t:library /r:c:\home\bin\AspectWeaver\AspectWeaver.Aspects.dll MyAspects.cs
AspectWeaver.Aspects.dllへの参照が必要になるので注意です。これで、注入するアスペクトも出来ました。
最後に、AspectWeaverで注入を行えば完了なのですが、AspectWeaverは設定ファイルを読み込んで動作するので、設定ファイルを用意します。
<?xml version="1.0" encoding="utf-8" ?> <Configuration logFile="LogWeaving.xml" cleanTempFiles="false"> <BaseAssembly>before\Calc.dll</BaseAssembly> <OutputAssembly>after\Calc.dll</OutputAssembly> <AspectAssemblies> <AspectAssembly uniqueName="MyAspects.dll" path="aspects\MyAspects.dll" /> </AspectAssemblies> </Configuration>
入出力ファイルを指定しているだけの簡単なものですね。このファイルは現在作業しているプロジェクトのトップ(つまり、before/after/aspectsの親ディレクトリ)に置きます。名前はconfig.xmlにでもしましょう。
で、実行
$ AspectWeaver.exe config.xml
以下のメッセージが出力されたら成功です。
Weaved successfully!
早速、afterディレクトリに移動してみましょう。Calc.dllがありますね。では、このCalc.dllを使って見ましょう。
using System; class Program { public static void Main () { Calc calc = new Calc (); Console.WriteLine (calc.Plus (10, 20)); Console.WriteLine (calc.Minus (10, 20)); calc.Print (10, 20); } }
これをコンパイルして実行すると・・・
Calling method 'P': System.Int32 Calc::Plus(System.Int32,System.Int32)
Calling method: System.Int32 Calc::Plus(System.Int32,System.Int32)
Arguments: 10, 20
30
Calling method: System.Int32 Calc::Minus(System.Int32,System.Int32)
Arguments: 10, 20
-10
Calling method 'P': System.Void Calc::Print(System.Int32,System.Int32)
Calling method: System.Void Calc::Print(System.Int32,System.Int32)
Arguments: 10, 20
(10, 20)
見事メソッド呼び出しに割り込んでいますね。'P'で始まるメソッドは2回割り込まれていることにも注目です。
駆け足で紹介しましたが、如何でしたでしょうか?