.net中实现AOP(二)

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

 

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

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

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

public enum AspectManagedType
    {
        ErrorHandle,
        ElapsedTime
    }
      

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

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

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

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

 

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

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

 

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

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

       

代码
 /// <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;
  }

      

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

   

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

public abstract class AspectObject  : ContextBoundObject
 {
 }

    
      第四:客户端调用。
     
           1:

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

 

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

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

           3:方法级的示例:

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

 

      总结:

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

 

 

posted on 2010-04-26 21:46  min.jiang  阅读(5577)  评论(6编辑  收藏  举报