続・プロクシを使ってAOP(OOP編)

誰も香取のケツなんてみたくねーよ、柴咲コウのはだ(ry
ガリレオ「壊死る(くさる)美しき天才殺人者の危険な誘惑」をみて)


釣りじゃないよ?w


前回勉強を兼ねて作ったProxyFactoryクラス。

public override IMessage Invoke(IMessage msg)
{
    IMethodCallMessage call = (IMethodCallMessage)msg;
    IMethodReturnMessage rtnMessage =
        RemotingServices.ExecuteMessage(target, call);
    return rtnMessage;
}

おおざっぱに↑こんな風にしてたのでこれあまり意味ないなぁということでちゃんと?してみる。
どうしましょ?
やりたいのは、ExecuteMessage()メソッドの前後に動的に処理を追加したいと。
「動的に処理」←ここがAdviceになる。

(ここにとある処理)
ExecuteMessage() // ←本来のメソッドの処理
(ここにとある処理)

みたいにできれば実現できそう。
ていうかAOPの基本がこれだけど^^;
だもんでAdviceを実現するクラスはこうなる。

using System;
using System.Diagnostics;
using AopProxySample.Aop;

namespace AopProxySample.Aop.Interceptor
{
    /// <summary>
    /// サンプルインターセプタクラス.
    /// </summary>
    [AttributeUsage(AttributeTargets.Method)]
    public class StubInterceptorAttribute : Attribute, IMethodInterceptor
    {
        public object Invoke(IMethodInvocation invocation)
        {
            Debug.WriteLine("AOP Before");
            object o = invocation.Proceed();
            Debug.WriteLine("AOP After");
            return o;
        }
   }
}

このAdviceを適用すると、実際の処理前に「AOP Before」、あとに「AOP After」とコンソールに出力。
Adviceとしての振る舞いはIMehtodInterceptorに定義。

using System;

namespace AopProxySample.Aop
{
    public interface IMethodInterceptor
    {
        object Invoke(IMethodInvocation invocation);
    }
}

このインタフェースを通じてプロクシクラスは処理を委譲することに。
IMethodInvocationインタフェースはインターセプトしているメソッド情報を格納。
このあたりのインタフェース名はS2AOPからパクリますた(´・ω・`)
IMethodInvocationの実装クラスでAdviceから呼び出すProceed()を実装。
ここが大本のExecuteMessage()メソッドを隠匿する箇所。

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Reflection;

namespace AopProxySample.Aop.Impl
{
    /// <summary>
    /// メソッド実行クラス.
    /// プロクシ経由で実行されるメソッドの情報を持つ
    /// </summary>
    public class MethodInvocation : IMethodInvocation
    {
        private MarshalByRefObject _target;
        private IMessage _message;
        public MarshalByRefObject Target
        {
            get { return _target; }
        }
        public MethodBase Method
        {
            get { return ((IMethodCallMessage)Message).MethodBase; }
        }
        public object[] Arguments
        {
            get { return ((IMethodCallMessage)Message).Args; }
        }
        private IMessage Message
        {
            get { return _message; }
        }
        public MethodInvocation(MarshalByRefObject target, IMessage message)
        {
            this._target = target;
            this._message = message;
        }
        public object Proceed()
        {
            return RemotingServices.ExecuteMessage(Target, (IMethodCallMessage)Message);
        }
    }
}

Target,Method,ArgumentsプロパティはS2AOPの説明で書いてあったのでまねしてみた。
実際ここらの情報をつかってパラメータの書き換え、追加みたいなのをやると思うんだけど…まだできてない。
(たぶん、ExecuteMessage()メソッドあたり何かすると思う)


これで最低限?のAOP実現できそうなクラスは完成(としておくw)。
Adviceを作るにはAttributeを作成しなくちゃいけないんで、実装するときにAttributeとIMethodInterceptorを書かなくちゃいけないのがいけてない感じorz
かといって両方実装した基底クラス作るのもどーなんだろ?


最後に書き直したProxyFactoryクラスはこんな感じ。

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Reflection;

using AopProxySample.Aop.Impl;

namespace AopProxySample.Aop.Proxies
{
    /// <summary>
    /// AOPプロクシクラス
    /// </summary>
    public class AopProxyFactory : RealProxy
    {
        public static T CreateInstance<T>() where T : MarshalByRefObject
        {
            T target = Activator.CreateInstance<T>();
            AopProxyFactory proxy = new AopProxyFactory(typeof(T));
            return (T)proxy.GetTransparentProxy();
        }

        private readonly MarshalByRefObject target;

        private AopProxyFactory(Type type) : base(type)
        {
            target = (MarshalByRefObject)Activator.CreateInstance(type);
        }
        public override IMessage Invoke(IMessage msg)
        {
            IMethodReturnMessage rtnMessage = null;
            IMethodCallMessage call = (IMethodCallMessage)msg;
            foreach (Attribute attr in
                call.MethodBase.GetCustomAttributes(typeof(Attribute), false))
            {
                rtnMessage = (IMethodReturnMessage)((IMethodInterceptor)attr).Invoke(
                                new MethodInvocation(target, msg));
                // 例外時の処理
            }
            return rtnMessage;
        }
    }
}

カスタムアトリビュート取得してIMethodInterceptor.Invoke()そのまま呼んでるけど、ここはis判定とかすべきはず。
サンプルなんでないだけてことで。


こんな感じで作ってみたけど、はじめから提供されてるS2AOP(S2Container)やSpring.NETやった方が便利だね。
仕事によってはフレームワーク使えないとかがあって、その上でどうしてもAOPやらやりたいってときの最終手段に覚えておくといいかも。
ってのが今回の動機。


いじょ、チラシの裏ですた。