.NET中通过代理实现面向方面编程(AOP)

来源:互联网 发布:从request取数组 编辑:程序博客网 时间:2024/06/18 00:23

AOP是OOP的延续,Aspect Oriented Programming的缩写,即面向方面编程。AOP是GoF设计模式的延续,设计模式追求的是调用者和被调用者之间的解耦,AOP也是这种目标的一种实现。

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

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

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

服务代理通常又分为以下两种:

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

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

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

调用关系图

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

实现AOP方法拦截的原理图

自定义异常代理类:

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

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

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

  1. /// <summary>
  2. /// Aspect代理,在这个类里面,实现对方法的拦截
  3. /// </summary>
  4. public class AspectProxyErrorLog : RealProxy
  5. {
  6. AspectManagedAttribute attr;
  7. /// <summary>
  8. /// 默认构造函数
  9. /// </summary>
  10. public AspectProxyErrorLog() : base()
  11. {
  12. }
  13. /// <summary>
  14. /// 构造函数
  15. /// </summary>
  16. /// <param name="myType">被代理的类的类型</param>
  17. public AspectProxyErrorLog(Type myType) : base(myType)
  18. {
  19. }
  20. /// <summary>
  21. /// 构造函数
  22. /// </summary>
  23. /// <param name="myType">被代理的类的类型</param>
  24. /// <param name="obj">被代理的对象</param>
  25. public AspectProxyErrorLog(Type myType,MarshalByRefObject obj) : base(myType)
  26. {
  27. target=obj;
  28. }
  29. MarshalByRefObject target;
  30. ILog LogManage;
  31. /// <summary>
  32. /// 当在派生类中重写时,在当前实例所表示的远程对象上调用在所提供的 IMessage 中指定的方法。<br />
  33. /// WebsharpAspect在这里执行对方法执行的拦截处理
  34. /// </summary>
  35. /// <param name="msg">IMessage,包含有关方法调用的信息。</param>
  36. /// <returns>调用的方法所返回的消息,包含返回值和所有 out 或 ref 参数。</returns>
  37. public override IMessage Invoke(IMessage msg)
  38. {
  39. IMessage retMsg=null ;
  40. IMethodCallMessage methodCall = (IMethodCallMessage)msg;
  41. IMethodReturnMessage methodReturn = null;
  42. object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
  43. methodCall.Args.CopyTo(copiedArgs, 0);
  44. object[] attrs = null;
  45. CoustomerErrorHandleAttribute ceha = null;
  46. if (msg is IConstructionCallMessage)
  47. {
  48. IConstructionCallMessage ccm = (IConstructionCallMessage)msg;
  49. RemotingServices.GetRealProxy(target).InitializeServerObject(ccm);
  50. ObjRef oRef = RemotingServices.Marshal(target);
  51. RemotingServices.Unmarshal(oRef);
  52. retMsg = EnterpriseServicesHelper.CreateConstructionReturnMessage(ccm, (MarshalByRefObject)this.GetTransparentProxy());
  53. }
  54. else
  55. {
  56. IMethodCallMessage mcm = (IMethodCallMessage)msg;
  57. attrs = methodCall.MethodBase.GetCustomAttributes(typeof(CoustomerErrorHandleAttribute), false);
  58. ceha = LogManagerFactory.GetCoustomerErrorHandleAttribute(attrs, methodCall.MethodBase.Name );
  59. if (null != ceha)
  60. {
  61. LogManage = ceha.ILogName;
  62. }
  63. try
  64. {
  65. object returnValue = methodCall.MethodBase.Invoke(this.target, copiedArgs);
  66. methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall);
  67. }
  68. catch (Exception ex)
  69. {
  70. if (null != ex.InnerException)
  71. {
  72. methodReturn = new ReturnMessage(ex.InnerException, methodCall);
  73. }
  74. else
  75. {
  76. methodReturn = new ReturnMessage(ex, methodCall);
  77. }
  78. }
  79. retMsg = methodReturn;
  80. }
  81. if (null != methodReturn)
  82. {
  83. if (null != methodReturn.Exception )
  84. {
  85. if (null != this.LogManage )
  86. {
  87. this.LogManage.Error(ceha .MethodErrorText + methodReturn.Exception.ToString());
  88. }
  89. }
  90. }
  91. return retMsg;
  92. }
  93. }

延伸阅读

面向切面编程:Aspect Oriented Programming

AOP是OOP的延续,是(Aspect Oriented Programming)的缩写,意思是面向切面编程。

主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。

在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。

它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

 

 应用场景:日志,安全、验证、事务、性能计数

mix结全方式:委托、事件、配置注册方式、Attibute

使用人员:框架人员

 

方面1:A1:主要业务流程,执行逻辑

方面2:B1、B2

 

传统结合方式:

B1:验证、权限

A1

B2:日志、异常处理

B1与B2结合:性能、事务

 

aop结合方式:Attibute、委托、事务一般是事件注册、接口,注入方式Microsoft.Practices.Unity.InterceptionExtension,ICallHandler

运行编译方式:静态编织和动态编织。

底层IL方式,一般语言扩展方式。

 

 

 public static void Counting(string countingKey, Action block)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();

            block();


            sw.Stop();

            int elapsedMilliseconds = sw.ElapsedMilliseconds > int.MaxValue ? int.MaxValue : (int)sw.ElapsedMilliseconds;
            Counter.AddCounter(countingKey, elapsedMilliseconds);
        }

 

internal class CountingCallHandler : ICallHandler
    {
        private string _countingKey;

        public CountingCallHandler(string countingKey)
        {
            _countingKey = countingKey;
        }

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            IMethodReturn retValue = null;
            Aspect.Counting(_countingKey, () =>
            {
                retValue = getNext()(input, getNext);
            });
            return retValue;
        }

        public int Order { get; set; }
    }
}

 

public class CountingHandlerAttribute : HandlerAttribute
    {
        public CountingHandlerAttribute(string countingKey)
        {
            this.CountingKey = countingKey;
        }

        public string CountingKey { get; set; }

        public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)
        {
            CountingCallHandler handler = new CountingCallHandler(this.CountingKey);
            return handler;
        }
    }

 

       [CountingHandler("FlightIntl.FlightSearch.SearchFlights")]

public void test(){
}

 

AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点(如Java),AOP具体实现有以下几个项目:
AspectJ (TM): 创建于Xerox PARC. 有近十年历史,成熟
缺点:过于复杂;破坏封装;需要专门的Java编译器。
动态AOP:使用JDK的动态代理API或字节码Bytecode处理技术。
基于动态代理API的具体项目有:
JBoss 4.0 JBoss 4.0服务器
基于字节码的项目有:
aspectwerkz ,spring

 

 

unity 实现aop

http://www.cnblogs.com/chsword/archive/2009/04/28/unity_aop.html

 

 

 

http://blog.csdn.net/yanghua_kobe/article/details/6917228