net中利用代理实现AOP,AOP微型框架实现10bet手机官网:
分类:多线程

 

昨夜一名CYQ.Data 框架爱好者在测试V5Beta版本时,向我发一个问题, 说F5下正常,直接运行exe会报错,于是,一夜深究后,就有了今天的博文。

      .net中利用代理实现AOP

    上篇文章我大概说了一下在.net中如何利用自定义代理类来实现AOP,那篇文章主要说了些原理,这篇我们直接上代码。

在前面的系列文章中,我介绍了消息、代理与AOP的关系,这次将我自己实现的一个AOP微型框架拿出来和大家交流一下。

 

     上篇文章我说到了在代码中可以利用泛型委托来封装异常处理,这样可以让程序看起来更加清晰,要想完成功能需要调用者调用指定的工厂方法才行,但要想改变某些程序员的编码习惯我想是一件比较困难的事情。有朋友说利用委托来实现异常处理并不算是真正意义上的AOP,因为传统的AOP并不需要客户端做代码结构的变更,最多也就是配置上的问题。但在.net中要想实现AOP,我想最方便的实现机制要属代理机制了,但只要利用代理,在性能上就会造成一定的影响。

 

AOP的最基本功能就是实现特定的预处理和后处理,我通过代理实现了此微型框架。

当你运行一段代码、一个软件,在运行时你发现:Debug下正常, Release下F5运行也正常,唯独Release编绎后单独运行异常,你会怎么想?

      如果开发过分布式服务,像remotion,wcf等,消息都是它们通信的重要手段。客户端通过方法调用形式体现的服务访问需要转换成具体的消息,然后经过编码才能利用传输通道发送给服务端,服务执行的结果也只能以消息的形式返回给调用方。

     第一:AspectManagedAttribute特性。说到代理机制,那么它直接针对的应该是一个类而不是具体的方法,所以这里我们实现AOP的最小单位就是类,我们定义一个AspectManagedAttribute特性,它标识了具体类会受到AOP管理。
   
     说明:
    
        1:由于这个特性允许标识在类上,所在需要加上[AttributeUsage(AttributeTargets.Class)]。

先来看看构成此微型框架的4个.cs文件。

 

      这些分布式服务有一共同特点:都通过代理方法间接的调用服务。服务代理,它自身并不提供服务的实现,只是起到一个中介作用,客户端把服务请求发送给服务代理,服务代理再去调真正的服务,同样服务返回时,也是返回给服务代理,再由服务代理返回给客户端。看到这,我想对于实现AOP的拦截就有点眉目了。在.net中,我们可以写自定义的RealProxy来实现AOP的方法拦截功能。     

        2:AOP可以实现多种非业务逻辑的"横切点",这里我们给AspectManagedAttribute增加一个枚举AspectManagedType,这里分为两种:

1.CommonDef.cs 用于定义最基本的AOP接口

这个问题对我而言,脑海里不曾有过,于是蛋定思蛋,先百度了一下:发现网上信息并不多,偶尔几条也是VC++的,对于C#的,绝无仅无。

      服务代理通常又分为以下两种:   
     1:透明代理。客户端在跨任何类型的远程处理边界使用对象时,对对象使用的实际上是透明代理。透明代理使人以为实际对象驻留在客户端空间中。它实现这一点的方法是:使用远程处理基础结构将对其进行的调用转发给真实对象。透明代理本身由 RealProxy 类型的托管运行时类的实例收容。RealProxy 实现从透明代理转发操作所需的部分功能。代理对象继承托管对象(例如垃圾回收、对成员和方法的支持)的关联语义,可以将其进行扩展以形成新类。这样,该代理具有双重性质,一方面,它需要充当与远程对象(透明代理)相同的类的对象;另一方面,它本身是托管对象。

           1》:普通的异常捕获,当请求的方法抛出异常时,记录在日志中,同时返回空对象。
           2》:在普通异常捕获的基础上,增加方法调用的时间记录。 

/************************************* CommonDef.cs **************************

 

     2:真实代理。RealProxy来实现与远程服务进行通信,所以这里就是我们实现AOP的地方。

public enum AspectManagedType
    {
        ErrorHandle,
        ElapsedTime
    }

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

10bet手机官网 1 

 

      

namespace EnterpriseServerBase.Aop
{
 ///
 /// IAopOperator AOP操作符接口,包括前处理和后处理
 /// 2005.04.12
 ///
 public interface IAopOperator
 {
  void PreProcess(IMessage requestMsg ) ;
  void PostProcess(IMessage requestMsg ,IMessage Respond) ;
 }

 

     下图是透明代理与真实代理以及远程对象的调用关系图:

         3》:继承ProxyAttribute:指示对象类型需要自定义代理。

 ///
 /// IAopProxyFactory 用于创建特定的Aop代理的实例,IAopProxyFactory的作用是使AopProxyAttribute独立于具体的AOP代理类。
 ///
 public interface IAopProxyFactory
 {
  AopProxyBase CreateAopProxyInstance(MarshalByRefObject obj ,Type type) ;
 }

当我把代码发给一友人,让他也帮忙试下时,发现对方的win7 64位竟然运行正常,灵异性又提升了一个等级。

                             10bet手机官网 2

         4》:重写ProxyAttribute的方法:MarshalByRefObject CreateInstance(Type serverType)。根据不同的AspectManagedType创建不同的自定义代理类。         

}

 

 

10bet手机官网 310bet手机官网 4代码

  1. AopProxyBase  AOP代理的基类,所有自定义AOP代理类都从此类派生,覆写IAopOperator接口,实现具体的前/后处理 。

面对这神一般的灵异Bug,我费了N个小时折腾,找一个不能调试的Bug,因为F5下都是正常的,那个辛苦啊。

      下图是利用自定义的RealProxy实现AOP方法拦截的原理图:

public override MarshalByRefObject CreateInstance(Type serverType)
        {
            MarshalByRefObject mobj = base.CreateInstance(serverType);
            if (aspectManaged)
            {
                RealProxy realProxy = this .GetCurrentRealProxy  (this.aspectManagedType , serverType, mobj);
                MarshalByRefObject retobj = realProxy.GetTransparentProxy() as MarshalByRefObject;
                return retobj;
            }
            else
            {
                return mobj;
            }

using System;
using System.Runtime.Remoting ;
using System.Runtime.Remoting.Proxies ;
using System.Runtime.Remoting.Messaging ;
using System.Runtime.Remoting.Services ;
using System.Runtime.Remoting.Activation ;

 

 

        }
        private RealProxy GetCurrentRealProxy(AspectManagedType _aspectManagedType, Type serverType, MarshalByRefObject mobj)
        {
            RealProxy real = null;
            if (AspectsManager.AspectsProxy.ContainsKey(serverType.Name))
            {
                return AspectsManager.AspectsProxy[serverType.Name];
            }
            switch (_aspectManagedType)
            {
                case AspectManagedType .ElapsedTime :
                    real = new AspectProxyElapsedTime(serverType, mobj);
                    break;
                case AspectManagedType.ErrorHandle :
                    real = new AspectProxyErrorLog(serverType, mobj);
                    break;
                default :
                    real = new AspectProxyErrorLog(serverType, mobj);
                    break;
            }
            lock (AspectsManager.AspectsProxy)
            {
                AspectsManager.AspectsProxy.Add(serverType.Name, real);
            }
            return real;
        }   

namespace EnterpriseServerBase.Aop
{
 ///
 /// AopProxyBase 所有自定义AOP代理类都从此类派生,覆写IAopOperator接口,实现具体的前/后处理 。
 /// 2005.04.12
 ///
 public abstract class AopProxyBase :  RealProxy ,IAopOperator
 {
  private readonly MarshalByRefObject target ; //默认透明代理
  
  public AopProxyBase(MarshalByRefObject obj ,Type type) :base(type)
  {
   this.target = obj ;
  }
  
  #region Invoke
  public override IMessage Invoke(IMessage msg)
  {
   bool useAspect = false ;
   IMethodCallMessage call = (IMethodCallMessage)msg ;

灵异现象已经说了,那灵异点是什么呢?标题已经出卖了这个问题,和Aop的RealProxy有关。

                            10bet手机官网 5

 

   //查询目标方法是否使用了启用AOP的MethodAopSwitcherAttribute
   foreach(Attribute attr in call.MethodBase.GetCustomAttributes(false))
   {
    MethodAopSwitcherAttribute mehodAopAttr = attr as MethodAopSwitcherAttribute ;
    if(mehodAopAttr != null)
    {
     if(mehodAopAttr.UseAspect)
     {
      useAspect = true ;
      break ;
     }
    }
   }

 

         自定义异常代理类:

        5:把所有的CustomProxy放入静态变量中,有小小的性能提升。这里我们创建了AspectsManager,代码比较简单,只包含一个管理器容器。     

   if(useAspect)
   {
    this.PreProcess(msg) ;
   }

光说AOP,园子里懂的人不多,用的估计也不多,所以深入无谓,浅出又难,只好简单过过场。 

         说明:1>自定义的代理类需要继承RealProxy。

public  class AspectsManager
 {
  public  static Dictionary<string , RealProxy> AspectsProxy = new Dictionary<string , RealProxy>();
 } 

   //如果触发的是构造函数,此时target的构建还未开始
   IConstructionCallMessage ctor = call as IConstructionCallMessage ;
   if(ctor != null)
   {
    //获取最底层的默认真实代理
    RealProxy default_proxy = RemotingServices.GetRealProxy(this.target) ;

 

                2>从 RealProxy 继承时,必须重写 Invoke方法。

 

    default_proxy.InitializeServerObject(ctor) ;
    MarshalByRefObject tp = (MarshalByRefObject)this.GetTransparentProxy() ; //自定义的透明代理 this

我是怎么发现问题是在Aop RealProxy?

                3>下面代码中的LogManage是一个log4net接口,我们可以把异常统一记录到日志中,供日后分析。

      第二:自定义代理类的实现,这里我就贴异常处理的AspectProxyErrorLog:

    return EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor,tp,',',');    
   }   
   
   IMethodReturnMessage result_msg = RemotingServices.ExecuteMessage(this.target ,call) ; //将消息转化为堆栈,并执行目标方法,方法完成后,再将堆栈转化为消息
   
   if(useAspect)
   {
    this.PostProcess(msg ,result_msg) ;
   }

这个使用常规方法,在代码段里插一些弹出信息,来缩小出错的代码片断,最终找到了它。

10bet手机官网 610bet手机官网 7代码

      说明:
  
        1:需要继承RealProxy,它提供代理的基本功能。 
        2:重写RealProxy的IMessage Invoke(IMessage msg),对当前实例所表示的远程对象调用在所提供的 IMessage 中指定的方法。

   return result_msg ;

 

/// <summary>
    /// Aspect代理,在这个类里面,实现对方法的拦截
    /// </summary>
    public class AspectProxyErrorLog : RealProxy    
    {
        AspectManagedAttribute attr;
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public AspectProxyErrorLog() : base()
        {
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="myType">被代理的类的类型</param>
        public AspectProxyErrorLog(Type myType) : base(myType)
        {
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="myType">被代理的类的类型</param>
        /// <param name="obj">被代理的对象</param>
        public AspectProxyErrorLog(Type myType,MarshalByRefObject obj) : base(myType)
        {
            target=obj;
        }
        MarshalByRefObject target;
        ILog LogManage;
        /// <summary>
        /// 当在派生类中重写时,在当前实例所表示的远程对象上调用在所提供的 IMessage 中指定的方法。<br />
        /// WebsharpAspect在这里执行对方法执行的拦截处理
        /// </summary>
        /// <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)
            {

       

  }
  #endregion

为了减少废话,这里直接讲述两点:

                IConstructionCallMessage ccm = (IConstructionCallMessage)msg;
                RemotingServices.GetRealProxy(target).InitializeServerObject(ccm);
                ObjRef oRef = RemotingServices.Marshal(target);
                RemotingServices.Unmarshal(oRef);
                retMsg = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)this.GetTransparentProxy());

10bet手机官网 810bet手机官网 9代码

  #region IAopOperator 成员

 

            }
            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;

 /// <summary>
  /// 当在派生类中重写时,在当前实例所表示的远程对象上调用在所提供的 IMessage 中指定的方法。<br />
  /// WebsharpAspect在这里执行对方法执行的拦截处理
  /// </summary>
  /// <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 != this.LogManage)
                    {
                        this.LogManage.Error(ceha.MethodErrorText + ex.ToString());
                    }
                    methodReturn = new ReturnMessage(null, methodCall);
                }
                retMsg = methodReturn;
            }
   return retMsg;
  }

  public abstract void PreProcess(IMessage requestMsg) ;
  public abstract void PostProcess(IMessage requestMsg, IMessage Respond) ;
  #endregion

1:哪些代码有问题,哪里有问题?

            }
            if (null != methodReturn)
            {
                if (null != methodReturn.Exception )
                {
                    if (null != this.LogManage )
                    {
                        this.LogManage.Error(ceha .MethodErrorText  + methodReturn.Exception.ToString());
                    }

      

 }

以前我写过一篇文章:C# Aop简单扫盲及ORM实体类属性拦截示例,可以先理解下AOP,但是它是有问题的,就是今天的问题。

                }
            }
            return retMsg;

          3:当捕获到异常后,首先将异常信息写入日志,为了让方法不对外抛出异常,我们可以对返回信息做处理,让它直接返回null。不知道这样做是否妥当,大家多多提意见啊。

}

这里给出详细的错误代码,和注释错误点,代码通常都有点长,不容易看:

        }
    }

   

10bet手机官网 1010bet手机官网 11View Code 

 

     第三:.net中要想实现方法的捕获,当然少不了ContextBoundObject:定义所有上下文绑定类的基类。这里我们可以创建一个空类,然后继承ContextBoundObject,客户端的类只需要做下继承就行。  

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

 

public abstract class AspectObject  : ContextBoundObject
 {
 }

    internal class AopAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            AopProxy proxy = new AopProxy(serverType);
            return (proxy.GetTransparentProxy() as MarshalByRefObject);
        }
    }
    internal class AopProxy : RealProxy
    {
        
        public AopProxy(Type serverType) : base(serverType)
        {
            
        }

           上面只是贴了部分代码,在下一篇中,我会对这部分代码做更加详细的分析。

    
      第四:客户端调用。      
           1:

        public override IMessage Invoke(IMessage msg)
        {
            IMessage message3;
            if (msg == null)
            {
                return msg;
            }
            if (msg is IConstructionCallMessage)
            {
                IConstructionReturnMessage message = base.InitializeServerObject((IConstructionCallMessage) msg);
                RealProxy.SetStubData(this, message.ReturnValue);
                return message;
            }
            if (!(msg is IMethodCallMessage))
            {
                return msg;
            }
            IMethodCallMessage mcm = msg as IMethodCallMessage;
            try
            {
                object[] args = mcm.Args;
                message3 = new ReturnMessage(mcm.MethodBase.Invoke(base.GetUnwrappedServer(), args), args, args.Length, mcm.LogicalCallContext, mcm);
            }
            catch (Exception exception)
            {
                throw new Exception(exception.Message);
            }
            return message3;
        }

作者:姜敏
出处: 

[AspectManaged(aspectManagedType=AspectManagedType.ErrorHandle  )]
public class GetMemberInfoProxy : AspectObject 

      
    }
    [AopAttribute]
    public class Users : ContextBoundObject
    {
        public Users()
        {

 

 

        }
        int i = 0;//任何的成员变量,在被Aop调用的时候,都会报“未将对象引用到对象实例的错误”
        public void Test()
        {
            try
            {
                i = 1;
            }
            catch (Exception err)
            {
                System.Windows.Forms.MessageBox.Show(err.Message);
            }
        }

 

           2:如果项目比较多,我们希望把日志能记录到不同的位置,往往我们会创建多个ILog接口,这里可以在方法上下手,创建一个方法级的CoustomerErrorHandle。主要提供如下几个属性:

 

              1>:public ILog ILogName { get; set; } 提供自定义的日志接口。当然ILog不能体现在Atttribute中,我们可以采用枚举来替换,然后在后端来动态创建ILog接口。
              2>:public string MethodElapsedTimeText { get; set; }  方法耗时的提示语。当此参数没有时,默认为方法名。
              3>: public string MethodErrorText { get; set; } 方法出异常时的提示语。当此参数没有时,默认为方法名。

通常大量的代码测试及分析方法,我发现了,只要Aop对象涉及到内部成员变量,在Release编绎后运行就一定会报错。

           3:方法级的示例:

 

[CoustomerErrorHandle(MethodElapsedTimeText = "根据会员卡号读取会员信息用时:", MethodErrorText = "根据会员卡号读取会员信息异常:")]
public MemberInfo GetMemberInfo(long  cardNo)

2:如何解决。 

 

在历经各种无解后,我发现了,RealProxy还有另一种写法,而这另一种写法,竟然可以解决这个问题,代码如下:

      总结:

10bet手机官网 1210bet手机官网 13View Code 

            本人对本篇文章的AOP做了下性能测试,没发现多大的消耗,大家对AOP性能有啥看法,多多指教。

 
class AopAttribute : ProxyAttribute
    {

 

        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            AopProxy realProxy = new AopProxy(serverType, base.CreateInstance(serverType));
            return realProxy.GetTransparentProxy() as MarshalByRefObject;

作者:姜敏
出处: 

        }
    }
    class AopProxy : RealProxy
    {
        MethodInfo method;
        MarshalByRefObject _target = null;
        public AopProxy(Type serverType,MarshalByRefObject target)
            : base(serverType)
        {
            _target = target;
            method = serverType.GetMethod("Set", BindingFlags.NonPublic | BindingFlags.Instance);
        }
        public override IMessage Invoke(IMessage msg)
        {
            if (msg != null)
            {
                if (msg is IConstructionCallMessage)
                {
                    IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage;
                  
                    RealProxy defaultProxy = RemotingServices.GetRealProxy(_target);

 

                    //如果不做下面这一步,_target还是一个没有直正实例化被代理对象的透明代理,
                    //这样的话,会导致没有直正构建对象。
                    defaultProxy.InitializeServerObject(constructCallMsg);

 

                    //本类是一个RealProxy,它可通过GetTransparentProxy函数得到透明代理
                    return System.Runtime.Remoting.Services.EnterpriseServicesHelper.CreateConstructionReturnMessage(constructCallMsg, (MarshalByRefObject)GetTransparentProxy());

                }
                else if (msg is IMethodCallMessage)
                {
                    IMethodCallMessage callMsg = msg as IMethodCallMessage;
                    object[] args = callMsg.Args;

                    //System.Windows.Forms.MessageBox.Show(callMsg.MethodBase.ToString());

                    if (callMsg.MethodName.StartsWith("set_") && args.Length == 1)
                    {
                        method.Invoke(_target, new object[] { callMsg.MethodName.Substring(4), args[0] });//对属性进行调用
                    }
                    return RemotingServices.ExecuteMessage(_target, callMsg);
                }
            }
            return msg;
        }

 

 

3:Debug和Release还有F5,究竟隐藏怎样的秘密? 

我也不懂,这涉及更底层的问题,不在我深究的范围内,懂的人士可以留言给大伙解答下。

 

总结:

 

在编程的世界里,灵异总是无处不在的,但是找它灵异的原因,并且能找到一种解决这类事件的方法,是每个代码建造师必须具有能力。
至于为何会产生这样那样的灵异事件,在研究的范围内,可以追究原因,在非自身研究的领域,大伙看着办了。 

 

 

终于,在忙碌微博精灵系列软件的日子里,终于又抽空完成了一篇文章,感觉不容易啊。

 

 

本文由10bet手机官网发布于多线程,转载请注明出处:net中利用代理实现AOP,AOP微型框架实现10bet手机官网:

上一篇:一个自定义LABEL组件的C10bet手机官网: 下一篇:终于体会到了,异常的解决办法
猜你喜欢
热门排行
精彩图文