天天看點

一起談.NET技術,.NET中通過代理實作面向方面程式設計(AOP)

  如果開發過分布式服務,像remotion,wcf等,消息都是它們通信的重要手段。用戶端通過方法調用形式展現的服務通路需要轉換成具體的消息,然後經過編碼才能利用傳輸通道發送給服務端,服務執行的結果也隻能以消息的形式傳回給調用方。

  這些分布式服務有一共同特點:都通過代理方法間接的調用服務。服務代理,它自身并不提供服務的實作,隻是起到一個中介作用,用戶端把服務請求發送給服務代理,服務代理再去調真正的服務,同樣服務傳回時,也是傳回給服務代理,再由服務代理傳回給用戶端。看到這,我想對于實作AOP的攔截就有點眉目了。在.net中,我們可以寫自定義的RealProxy來實作AOP的方法攔截功能。

  服務代理通常又分為以下兩種:  

  1:透明代理。用戶端在跨任何類型的遠端處理邊界使用對象時,對對象使用的實際上是透明代理。透明代理使人以為實際對象駐留在用戶端空間中。它實作這一點的方法是:使用遠端處理基礎結構将對其進行的調用轉發給真實對象。透明代理本身由 RealProxy 類型的托管運作時類的執行個體收容。RealProxy 實作從透明代理轉發操作所需的部分功能。代理對象繼承托管對象(例如垃圾回收、對成員和方法的支援)的關聯語義,可以将其進行擴充以形成新類。這樣,該代理具有雙重性質,一方面,它需要充當與遠端對象(透明代理)相同的類的對象;另一方面,它本身是托管對象。

  2:真實代理。RealProxy來實作與遠端服務進行通信,是以這裡就是我們實作AOP的地方。

  下圖是透明代理與真實代理以及遠端對象的調用關系圖:

一起談.NET技術,.NET中通過代理實作面向方面程式設計(AOP)

  下圖是利用自定義的RealProxy實作AOP方法攔截的原理圖:

一起談.NET技術,.NET中通過代理實作面向方面程式設計(AOP)

  自定義異常代理類:

  說明:1>自定義的代理類需要繼承RealProxy。

     2>從 RealProxy 繼承時,必須重寫 Invoke方法。

     3>下面代碼中的LogManage是一個log4net接口,我們可以把異常統一記錄到日志中,供日後分析。

一起談.NET技術,.NET中通過代理實作面向方面程式設計(AOP)
一起談.NET技術,.NET中通過代理實作面向方面程式設計(AOP)

代碼

/// <summary>

    /// Aspect代理,在這個類裡面,實作對方法的攔截

    /// </summary>

    public class AspectProxyErrorLog : RealProxy    

    {

        AspectManagedAttribute attr;

        /// <summary>

        /// 預設構造函數

        /// </summary>

        public AspectProxyErrorLog() : base()

        {

        }

        /// 構造函數

        /// <param name="myType">被代理的類的類型</param>

        public AspectProxyErrorLog(Type myType) : base(myType)

        /// <param name="obj">被代理的對象</param>

        public AspectProxyErrorLog(Type myType,MarshalByRefObject obj) : base(myType)

            target=obj;

        MarshalByRefObject target;

        ILog LogManage;

        /// 當在派生類中重寫時,在目前執行個體所表示的遠端對象上調用在所提供的 IMessage 中指定的方法。<br />

        /// WebsharpAspect在這裡執行對方法執行的攔截處理

        /// <param name="msg">IMessage,包含有關方法調用的資訊。</param>

        /// <returns>調用的方法所傳回的消息,包含傳回值和所有 out 或 ref 參數。</returns>

        public override IMessage Invoke(IMessage msg)

            IMessage retMsg=null ;

            IMethodCallMessage methodCall = (IMethodCallMessage)msg;

            IMethodReturnMessage methodReturn = null;

            object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];

            methodCall.Args.CopyTo(copiedArgs, 0);

            object[] attrs = null;

            CoustomerErrorHandleAttribute ceha = null;

            if (msg is IConstructionCallMessage)

            {

                IConstructionCallMessage ccm 

= (IConstructionCallMessage)msg;

                RemotingServices.GetRealProxy(target).InitializeServerObject(ccm);

                ObjRef oRef = RemotingServices.Marshal(target);

                RemotingServices.Unmarshal(oRef);

                retMsg = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)this.GetTransparentProxy());

            }

else

                IMethodCallMessage mcm = (IMethodCallMessage)msg;                

                attrs = methodCall.MethodBase.GetCustomAttributes(typeof(CoustomerErrorHandleAttribute), false);               

                ceha = LogManagerFactory.GetCoustomerErrorHandleAttribute(attrs, methodCall.MethodBase.Name );

                if (null != ceha)

                {

                    LogManage = ceha.ILogName;

                }

                try

                    object returnValue = methodCall.MethodBase.Invoke(this.target, copiedArgs);

                    methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);

                catch (Exception ex)

                    if (null != ex.InnerException)

                    {

                        methodReturn = new ReturnMessage(ex.InnerException, methodCall);

                    }

                    else

                        methodReturn = new ReturnMessage(ex, methodCall);

                retMsg = methodReturn;

if (null != methodReturn)

                if (null != methodReturn.Exception )

                    if (null != this.LogManage )

                        this.LogManage.Error(ceha .MethodErrorText  + methodReturn.Exception.ToString());

return retMsg;

    }

  上面隻是貼了部分代碼,在下一篇中,我會對這部分代碼做更加詳細的分析。

繼續閱讀