DynamicAopProxy 更に変更

拙作の実装をベースにして更に標題のクラスを見直した。

現在の実装には効率上の弱点がある。それはメソッドをインターセプトする度に、毎回Aspectをセットアップしていることだ。一度や二度ならばさほど気にならないかもしれないが、例えば1000回呼ばれるようなメソッドにAspectを摘要する場合などは、影響が気になってくるだろう。 そこで、Aspectからインターセプタを生成するセットアップ処理は最初に一度だけ行い、その後メソッドインターセプト時にはセットアップ済みのAspectの実行だけを行うようにリファクタリングしてみた。

  • Aspectのセットアップを事前に行うためのメソッド"SetUpAspects"を用意
private void SetUpAspects()
{
    ArrayList interceptorList = new ArrayList();
    if (this.aspects != null)
    {
        MethodInfo methodInfos = this.type.GetMethods( 
            BindingFlags.Public | BindingFlags.NonPublic | 
            BindingFlags.Instance | BindingFlags.FlattenHierarchy);

        foreach (MethodInfo method in methodInfos)
        {
            if ( method.IsVirtual || this.type.IsInterface )
            {
                foreach (IAspect aspect in this.aspects)
                {
                    IPointcut pointcut = aspect.Pointcut;
                    if (pointcut == null || pointcut.IsApplied(method))
                    {
                        interceptorList.Add(aspect.MethodInterceptor);
                    }
                }
                if (interceptorList.Count > 0)
                {
                    IMethodInterceptor interceptors = (IMethodInterceptor[])
                    interceptorList.ToArray(typeof(IMethodInterceptor));
                    this.interceptors.Add(method, interceptors);
                }
            }
        }
    }
}

やっていることは予め型のメソッド情報を抽出して、対象と思われるメソッドに対してAspectの適用を行っているだけ。このメソッドはコンストラクタの最後で一度だけ呼び出せば良い。

メソッドのインターセプト時には、そのメソッド情報に対応した、インターセプタの配列があれば、実行するだけ。

public object Intercept(IInvocation invocation, params object args)
{
    object ret = null;
    if (this.interceptors.ContainsKey(invocation.Method))
    {
        IMethodInterceptor interceptors = 
        	this.interceptors[invocation.Method] as IMethodInterceptor[];
        IMethodInvocation mehotdInvocation =
           new DynamicProxyMethodInvocation(
           	invocation.InvocationTarget, this.type, invocation, args, interceptors, parameters);
        ret = interceptors[0].Invoke(mehotdInvocation);
    }
    else
    {
        ret = invocation.Proceed(args);
    }
    return ret;
}

ここ数日の思いつきで変更した内容なので、今後のテストで不具合が出るようであれば元に戻してしまうかもしれない。