在.NET中要实现AOP有多种办法,至少包含如下几种:
- 微软企业库Microsoft Enterprise Library
- postsharp(收费)
- ContextBoundObject
- Castle.Core
- Remoting的RealProxy
- ...
对于第一种,需要安装较大的类库,第二种收费,因此这两者使用相对没那么多,本文只比较后面三种的性能情况。
先看结果
三种方法分别使用AOP和非AOP得方式调用相同空方法1000000次的结果如下:
ContextBoundObject | Proxy | Castle | |
---|---|---|---|
Original: | 5399 | 4 | 4 |
Aop: | 8420 | 1456 | 48 |
可以看出,性能差距是巨大的,为什么有这么大的差距呢?接下来分析一下原理。
ContextBoundObject
通过ContextBoundObject来实现AOP,其中有大约60%的时间花在GetCustomAttribute的反射上,另外40%则是MarshalByRefObject(ContextBoundObject的基类,实际上ContextBoundObject什么事情都没做,只是单纯的继承了MarshalByRefObject)内部上下文管理上,它需要管理些啥呢?ContextBoundObject是一个上下文对象的基类,继承自它的子类将被一个特别的上下文管理起来,这个上下文包括一系列的属性集合或者规则,当进入或者离开上下文时,将强制执行规则。当使用ContextBoundObject实现AOP时,除了性能底下,其必须继承自ContextBoundObject,对于单继承的语言来讲可能带来不便。
Proxy
Proxy实现AOP会利用到RealProxy和TransparentProxy,它们分别是干啥的呢?
TransparentProxy:在 CLR 中在 IL 层面最大程度扮演被代理的远端对象,从类型转换到类型获取,从字段访问到方法调用。对 CLR 的使用者来说 TP 和被其代理的对象完全没有任何区别,只有通过 RemotingServices.IsTransparentProxy 才能区分两者的区别。
RealProxy:是提供给 CLR 使用者扩展代理机制的切入点,通过从 RP 继承并实现 Invoke 方法,用户自定义代理实现可以自由的处理已经被从栈调用转换为消息调用的目标对象方法调用,如实现缓存、身份验证、安全检测、延迟加载等等。
使用Proxy来实现AOP其消耗主要来自TP和RP两个对象的实例化以及“对象引用 -> TP -> RP -> 实际对象”的调用链。
Castle
Castle的本质是创建继承原来类的代理类,重写虚方法实现AOP功能。内部通过大量的Reflect.Emit + OpCode编程来实现高性能的保证,可以在很多开源项目中找到Castle的身影。