Quillを使うことにより簡単にDIやAOPを行うことができます。 Quillでは属性を用いてDIやAOPの設定を行います。 S2Container.NET 1.3.0から追加された機能です。
ステートレスな業務ロジックを構築するために作成された為、S2Container.NETに比べると機能が制限されています。 ただしS2Container.NETと連携してS2Container.NETに登録されているコンポーネントを利用する機能があります。
Quillを使うことにより簡単にDIやAOPを行うことができます。 Quillでは属性を用いてDIやAOPの設定を行います。 S2Container.NET 1.3.0から追加された機能です。
ステートレスな業務ロジックを構築するために作成された為、S2Container.NETに比べると機能が制限されています。 ただしS2Container.NETと連携してS2Container.NETに登録されているコンポーネントを利用する機能があります。
DIを行う為にはSeasar.Quill.QuillInjectorクラスのInjectメソッドに、DIを行わせたいオブジェクトを渡します。
QuillInjectorはInjectメソッドに渡されたオブジェクトのフィールドから自動的に判断して、 必要なオブジェクトをフィールドにバインドします。その際にあらかじめ設定ファイルでコンポーネントを登録しておく必要はありません。
QuillではQuillでインスタンスを管理するオブジェクトとS2Containerでインスタンスを管理するオブジェクトをDIすることができます。
Quillでインスタンスを管理するオブジェクトをDIする場合は、 フィールドの型にSeasar.Quill.Attrs.ImplementationAttributeクラスを属性として設定します。 Quillは設定された属性からバインドすべきと判断して自動的にバインドを行います。
初めてバインドされるオブジェクトは新しくインスタンス化されますが、 一度バインドされたオブジェクトは以後同じインスタンスがバインドされることになります。 つまりQuillでバインドするためにインスタンス化されるオブジェクトは全てsingletonになります。 これはQuillがステートレスな業務ロジックを構築することを目的に作られたからです。
下記の例ではIEmployeeLogicインターフェースの実装クラスであるEmployeeLogicクラスを Form1のフィールドにバインドします。もしEmployeeLogicクラスがフィールドを持ち、 そのフィールドの型にImplementationAttributeクラスが属性として設定されていれば連鎖的にバインディングが行われていきます。
static class Program { [STAThread] static void Main() { Form1 form = new Form1(); QuillInjector injector = QuillInjector.GetInstance(); injector.Inject(form); ・・(略)・・ } } public partial class Form1 : Form { // IEmployeeLogicの実装クラスであるEmployeeLogicのインスタンスがバインドされる protected IEmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } [Implementation(typeof(EmpployeeLogic))] public interface IEmployeeLogic { ・・(略)・・ } public class EmployeeLogic : IEmployeeLogic { ・・(略)・・ }
上記のようなインターフェースを用意せずに直接クラスのインスタンスをバインディングする場合は、 下記のようにImplementationAttributeクラスに引数を指定しません。
public partial class Form1 : Form { // EmployeeLogicのインスタンスがバインドされる protected EmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } [Implementation] public class EmployeeLogic { ・・(略)・・ }
また下記のように引数無しのImplementationAttributeクラスをインターフェースに指定して、 Aspectで実装を持たせることもできます。
public partial class Form1 : Form { // Aspectによって拡張された型のインスタンスがバインドされる protected IEmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } [Implementation] [Aspect(typeof(HogeInterceptor))] public interface IEmployeeLogic { ・・(略)・・ }
フィールドにSeasar.Quill.Attrs.BindingAttributeクラスが属性として指定されている場合は、 S2Containerでインスタンスを管理するオブジェクトをバインディングします。 BindingAttributeクラスの引数にはS2Containerのコンポーネント名を指定します。
下記の例ではS2Containerにコンポーネント名"empLogic"として定義されているコンポーネントのインスタンスを Form1のフィールドにバインドします。
static class Program { [STAThread] static void Main() { // 定義(dicon)ファイルをセットする SingletonS2ContainerFactory.ConfigPath = "App.dicon"; // S2Containerを初期化する SingletonS2ContainerFactory.Init(); Form1 form = new Form1(); QuillInjector injector = QuillInjector.GetInstance(); injector.Inject(form); ・・(略)・・ } } public partial class Form1 : Form { [Binding("empLogic")] protected IEmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } public interface IEmployeeLogic { ・・(略)・・ } public class EmployeeLogic : IEmployeeLogic { ・・(略)・・ }
App.dicon
<?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="empLogic" class="HogeNamespace.EmployeeLogic" /> </components>
QuillでAOPを利用する為にはSeasar.Quill.Attrs.AspectAttributeクラスを属性として設定します。 AspectAttributeクラスはクラス・インターフェース、メソッドに設定することができます。 ただしQuillによってインスタンス化されたオブジェクトにのみ設定が有効となります。
AspectAttributeクラスの第1引数にはInterceptorを指定します。 Quillでインスタンスを管理するInterceptorを指定する場合はInterceptorのTypeクラスを指定します。 S2Containerでインスタンスを管理するInterceptorを指定する場合はInterceptorのコンポーネント名を指定します。
クラス・インターフェースにAspectAttributeクラスを設定した場合は、 クラス・インターフェースに含まれるメソッドにAspectが適用されます。 ただしインターフェースに定義されたメソッド、virtualキーワードが指定されたメソッドが対象となります。
メソッドにAspectAttributeクラスを設定することでメソッド毎にAspectを適用することができます。 ただしインターフェースに定義されたメソッド、virtualキーワードが指定されたメソッドにしか設定することはできません。
Quillでインスタンスを管理するInterceptorを使用する場合は、 Seasar.Quill.Attrs.AspectAttributeクラスを属性として、 第1引数にInterceptorのTypeクラスを指定します。
Interceptorのインスタンスは必ずsingletonになります。
下記の例ではEmployeeLogicのGetEmployeeByEmpNoにAspectを適用します。 InterceptorのインスタンスはQuillで管理されます。
static class Program { [STAThread] static void Main() { Form1 form = new Form1(); QuillInjector injector = QuillInjector.GetInstance(); injector.Inject(form); ・・(略)・・ } } public partial class Form1 : Form { // EmployeeLogicのインスタンスがバインドされる protected EmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } [Implementation] public class EmployeeLogic { [Aspect(typeof(ConsoleWriteInterceptor))] public virtual Employee GetEmployeeByEmpNo(int empNo) { Console.WriteLine("メソッドが呼び出されました"); } ・・(略)・・ } public class ConsoleWriteInterceptor : IMethodInterceptor { #region IMethodInterceptor メンバ public object Invoke(IMethodInvocation invocation) { MethodBase method = invocation.Method; Console.WriteLine("メソッドを開始します"); // 実際の処理を実行する object ret = invocation.Proceed(); Console.WriteLine("メソッドを終了します"); return ret; } #endregion }
EmployeeLogicのGetEmployeeByEmpNoをForm1から実行すると下記のようなメッセージがコンソールに出力されます。
メソッドを開始します メソッドが呼び出されました メソッドを終了します
S2Containerでインスタンスを管理するInterceptorを使用する場合は、 Seasar.Quill.Attrs.AspectAttributeクラスを属性として、 第1引数にInterceptorのコンポーネント名を指定します。
下記の例ではEmployeeLogicのGetEmployeeByEmpNoにAspectを適用します。 InterceptorのインスタンスはS2Containerで管理されます。
static class Program { [STAThread] static void Main() { // 定義(dicon)ファイルをセットする SingletonS2ContainerFactory.ConfigPath = "App.dicon"; // S2Containerを初期化する SingletonS2ContainerFactory.Init(); Form1 form = new Form1(); QuillInjector injector = QuillInjector.GetInstance(); injector.Inject(form); ・・(略)・・ } } public partial class Form1 : Form { // EmployeeLogicのインスタンスがバインドされる protected EmployeeLogic employeeLogic; public Form1() { InitializeComponent(); } ・・(略)・・ } [Implementation] public class EmployeeLogic { [Aspect("consoleInterceptor")] public virtual Employee GetEmployeeByEmpNo(int empNo) { Console.WriteLine("メソッドが呼び出されました"); } ・・(略)・・ } public class ConsoleWriteInterceptor : IMethodInterceptor { #region IMethodInterceptor メンバ public object Invoke(IMethodInvocation invocation) { MethodBase method = invocation.Method; Console.WriteLine("メソッドを開始します"); // 実際の処理を実行する object ret = invocation.Proceed(); Console.WriteLine("メソッドを終了します"); return ret; } #endregion }
App.dicon
<?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="consoleInterceptor" class="HogeNamespace.ConsoleWriteInterceptor" /> </components>
EmployeeLogicのGetEmployeeByEmpNoをForm1から実行すると下記のようなメッセージがコンソールに出力されます。
メソッドを開始します メソッドが呼び出されました メソッドを終了します
Interceptorの作成方法はS2AOP.NETと同じです。下記のドキュメントを参照して下さい。
クラス・インタフェースに設定されたInterceptorは、メソッドに設定されたInterceptorより先に呼び出されます。 クラス・インターフェースに複数のAspectAttributeクラスが設定された場合、 もしくはメソッドに複数のAspectAttributeクラスが設定された場合は、 AspectAttributeクラスの第2引数に並び順を指定して呼び出される順番を制御することができます。 数字の小さい方が先に呼び出されます。第2引数を省略した場合は0が設定されたと判断します。 ただしメソッドに指定したInterceptorをクラス・インターフェースに設定されたInterceptorより先に呼び出すことはできません。
[Implementation] public class EmployeeLogic { [Aspect("consoleInterceptor", 1)] [Aspect(typeof(TraceInterceptor), 2)] public virtual Employee GetEmployeeByEmpNo(int empNo) { ・・(略)・・ } ・・(略)・・ }
Seasar.Quill.QuillInjectorクラスのInjectメソッドを用いてDIを行ってきましたが、 ツールボックスからコントロール(Seasar.Quill.QuillControl)をFormに貼り付けるだけで簡単にDIを利用することもできます。
Seasar.Quill.QuillControlを使う場合はツールボックスに登録する必要があります。 「ツールボックスアイテムの選択」からSeasar.Quill.dllを選択してください。
下記はForm1のフィールドにCulcLogicをバインドする例です。
Public partial class Form1 : Form { protected CulcLogic culcLogic = null; private void button1_Click(object sender, EventArgs s) { int ret = culcLogic.Plus(1, 2); Console.WriteLine(“戻り値:” + ret); } } [Implementation] Public class CulcLogic { [Aspect(typeof(ConsoleWriteInterceptor))] public virtual int Plus(int x, int y) { Console.WriteLine(“Plusが呼ばれました”); return x + y; } }
次にデザイナでQuillControlをForm1に貼り付けます。
これでフィールドのculcLogicにAspectが適用されたインスタンスがDIされるようになります。 実行して「Plusを呼ぶ」ボタンを押すとコンソールにメッセージが出力されます。
メソッドを開始します Plusが呼ばれました メソッドを終了します 戻り値:3