SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

AspectDNGで始める.NETのAOP

AspectDNGでS2Container.NETを機能拡張してみる

S2Container.NETのdiconファイルを拡張

 次に、もう少し実践的な例を説明していきます。

 AOPによる「既存の振る舞いへの作用」という側面を、S2Container.NETにおけるdiconファイルの処理を拡張する事で実施してみます。

 具体的に、AspectDNGを使ってdiconファイルのargproperty要素にJScript.NET式以外の、より表現方法が多彩な式言語を利用できるようにします。

補足
 S2Container.NETとは、JavaによるDIコンテナであるS2Container(Seasar2)の.NET実装版です。S2Container.NETのdiconファイルではJScript.NET式が記述できるようになっていますが、OGNL式が記述できるJavaに比べて制約が多くなっています(例:メソッド呼び出し結果を引数に指定できないなど)。ただし、最新のS2Container.NET 1.2.2では改良が進んでいますのでご注意ください。

必要な環境

 このサンプルの実行に必要になる環境は次のとおりです。

補足
 Spring.NETとは、JavaによるDIコンテナであるSpring Frameworkの.NET実装版です。今回は、Spring.NETのExpression Evaluation機能より、他に良さそうな式言語の実装を見つけられなかったため、Spring.NETを使っています。

アスペクトの定義クラス作成

 それでは、diconファイルのargproperty要素の処理を拡張するためのアスペクト定義クラスを作成していきます。

技術概要

 まず、S2Container.NETのdiconファイル処理の実装に関して、簡単に説明します。

 diconファイルのarg要素の処理は、Seasar.Framework.Container.Impl.ArgDefImplクラスのValueプロパティの取得(get)で実装されています。

 また、ArgDefImplクラスは、property要素を処理するPropertyDefImplクラスや、aspect要素を処理するAspectDefImplクラスの親クラスでもあるため、ArgDefImplValueプロパティの取得にアスペクトをウィービングすることで、argpropertyaspect要素の既存の振る舞いも変更することが可能になります。

 ところで、今回は、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の内容を渡しています。

ExpressionExtendAspect.cs
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」を指定するだけです。

Seasar.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」を再ビルドする方法をとることにします。

ウィービング後のSeasar.dllを参照して再ビルド
>csc /out:ExpressionAop.dll /target:library
 /r:aspectdng.exe;Seasar.dll;Spring.Core.dll ExpressionExtendAspect.cs
厳密な名前
 厳密な名前は、アセンブリの識別子と公開キー・デジタル署名で構成されるグローバルに一意な名前で、アセンブリに割り当てることが可能です。詳しくは.NETのドキュメント等を参照して下さい。

S2Container.NETを使った実行クラス作成

 ここではウィービング後の「Seasar.dll」を検証するために、S2Container.NETを使った実行クラスを作成していきます。

DI対象クラス作成

 まず、簡単なDI対象クラスを作成していきます。インターフェイスとしてIData、その実装クラスであるDataImplIDataを注入するクラスであるDataProcessorを用意しました。

IData.cs
public interface IData
{
    void Print();
}
DataImpl.cs
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);
    }
}
DataProcessor.cs
public class DataProcessor
{
    private IData data;

    public IData Data
    {
        set
        {
            this.data = value;
        }
    }

    public void Process()
    {
        this.data.Print();
    }
}

実行クラス作成

 app.diconのDI定義からDataProcessorを取得し、実行するクラスを作成します。

Tester.cs
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で参照するための値、nametestModeを定義します。

Tester.exe.config
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appSettings>
        <add key="name" value="チェックデータ" />
        <add key="testMode" value="false" />
    </appSettings>
</configuration>

diconファイル作成

 「Seasar.dll」へのウィービング効果を検証するため、通常のdiconファイルでは表現できない以下の点を取り入れます。

  • アプリケーション構成ファイルの値を加工した値を、コンストラクタの引数に指定する
  • アプリケーション構成ファイルの値によって注入するコンポーネントを変更する
注意
 この記事を執筆した段階のS2Container.NET 1.2.0ではアプリケーション構成ファイルの値を参照したりすることは出来ませんでしたが、S2Container.NET 1.2.2では、diconファイルのJScript.NET式でcontainerappSettingsが使用できるようになっています。

 data、testdata、processorの3つのコンポーネント定義を用意し、dataは一般的な記述、testdataはアプリケーション構成ファイル「Tester.exe.config」のnameの値にtestという文字列を繋げた文字列をコンストラクタの引数に指定、processorは、testModeの値がtrueの場合にコンポーネントtestdataを、falseの場合にコンポーネントdataをDataプロパティに注入しています。

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="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を使い始めてみてはいかがでしょうか。

参考資料

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

和田 好朗(ワダ ヨシアキ)

ITアーキテクト目指して奮闘中。新技術には目が無い技術者。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/446 2006/08/10 00:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー