Cavingdeep的.NET专栏

软件开发与工程,又一种艺术。
随笔 - 8, 评论 - 83, 引用 - 5

导航

工具

关于

逆水行舟,不进则退!

业精于勤,荒于嘻
行成于思,毁于随



月 [下月] [上月]


标签

每月存档

广告



访客

 

因为最近关注AOP的人越来越多,所以就阅读了一些关于AOP方面的文章,一篇个人认为比较好的文章是我在CSDN上看到的转载的一篇文章,转载者没有注明出处是哪里,所以我只好将转载网址贴在这里了《AOP及其Java实现机制》。如果您还没有听说过AOP或者还不怎么了解,欢迎首先阅读转载文章然后再继续这篇Blog。

由于目前我对Java的了解不是非常丰富,所以就不对Java方面的AOP作出评论了,但就.NET而言,我觉得AOP在.NET中的实现还是有一定的不理想因素的。

AOP思想

AOP的基本思想是将不同方面(Aspect)的代码分别开来写,达到逻辑清晰,可复用的目的。与OOP不同,AOP的核心是关注点(Concern),它将程序分为核心关注点(业务)与横切关注点(可能有纠结的功能,例如日志、事务等)。AOP并不与OOP冲突,OOP是AOP中实现业务的手段,我们主要用OOP来做核心关注点与横切关注点的实现,然后用AOP将这两种关注点合并产出最终程序。

AOP的实现

目前,AOP会通过织入器(Weaver)将不同方面的代码混合(织入),织入的方式多种多样,一般来说,只要是能够将两段毫不相干的代码混合在一起,那么就可以叫做织入了。不过,目前大多数产品的做法是采用反射、动态代理、元数据等方法来作为AOP的织入方法。我在下面要讲的,就是反驳大多数采用动态代理来作为AOP织入机制的产品,也就是我前面说到的AOP目前来说,至少在.NET中,还是不太理想的。

Aspect#

在.NET下也有不少的AOP框架,其中比较有名的当数Aspect#,但是我在对它进行了一定的使用测试后,发现了至少对我来说,它丝毫没有用处。它的实现机制就是大名鼎鼎的动态代理与反射。对于还不熟悉动态代理的朋友,它说白了就是“代理”模式的动态形态,可以动态的生成某个类的代理。至于代理模式,就是GoF中著名的23个设计模式之一的代理模式,还不明白的朋友赶快翻阅一下《Java与模式》吧!^_^

AOP中的代理模式

你也许会问,我到底对动态代理有什么不满呢?没有,我对动态代理没什么不满,如果使用正确的话,动态代理还是很有价值的。不过,要想作为AOP的万能织入器的其中一种,动态代理就不是那么可靠了。熟悉代理模式的朋友一定都知道代理模式是靠继承(或对“基接口”的实现)实现代理的,那么如果一个类不可以被继承呢?Bingo,这时代理模式就不成立了,也就是说,如果你用Aspect#对一个sealed类进行Mixin的话(混合以便动态增添新功能)那么它就会失败,因为无法创建动态代理。这到底是怎么回事呢?让我们来看看代理模式(在这里更准确的说应该是装饰模式)是如何做到织入新代码的:

public class A { public A() {} } public interface ILog { void Log(); } public class ProxyForA : A, ILog { public ProxyForA() {} void ILog.Log() { //...... } } A a = new ProxyForA(); ILog log = (ILog) a; log.Log();

就像你看到的上面的代码,动态代理会动态的生成这个Proxy类,并且通过一个Wrapper来将它返回,作为一个A看待,这时看不到ProxyForA存在的你可能会认为真的很神奇,将A的行为扩展了,但当A是sealed类时利用动态代理的弊端就暴露了,因为A是不可以被继承的。这也从另一面证实了一个OO设计原则的重要性:DIP(Dependence Inversion Principle),这个原则基本上告诉我们要以抽象概念做设计,不是以具体,这样,我们就永远都有可能使用代理模式了。关于OO设计原则,我在CSDN上的Blog有一篇总结它们的文章,感兴趣的朋友可以参阅一下^_^

那么现在让我们来看看AOP中的另一个织入手段,拦截器(Interceptor)。拦截器可以根据一个或多个切入点找到要切入的代码部分然后将横切方面(日志、事务等)代码织入,这个织入手段同Mixin一样,本身没有什么问题,问题出在在Aspect#中也是通过代理来实现织入的,这样就又带来了一个新的问题,什么问题呢?虽然目前.NET下AOP框架产品对于切入点的支持不是很广泛,但这也只是迟早的事,问题还是没有的,真正的问题就出在使用代理模式上面,让我们来看看如何通过代理模式在已有的类成员中实现织入:

public class A { public virtual void Do() { //.... } } public class ProxyForA : A { private IInterceptor _interceptor = new LogInterceptor(); public ProxyForA() { _interceptor.Instance = this; } public override void Do() { bool proceed = _interceptor.Intercept(); if (proceed) { base.Do(); } } }

使用动态代理实现切入点的织入就是以以上方式实现的,可以很清楚的看到,这只在A类中的Do方法为virtual时才可以实现,如果某个要切入的成员不是virtual,那么织入就会失败。

总结

以上所谈到的两个使用动态代理的缺陷都是很严重的,如果非要改动代码来适应动态代理的话就会破坏面向对象的设计,严重的甚至可以带来安全隐患。

正因为上述原因,我觉得Aspect#不是一个很理想的产品。个人觉得一种较好的AOP织入实现方式是代码在编译前的替换,这样可以避免所有限制与问题,但缺点是非常难实现。目前我觉得使用AOP框架倒不如自己的好设计,如果设计的得体,那么同样可以后期通过代理、装饰等模式对系统的行为进行快速方便的扩展。同时我也很期待新的技术的出现来使AOP成为现实!

相关文章

打印 | 张贴于 2005-03-28 17:17:00 | Tag:Designs

留言反馈

#re: AOP在.NET中的现实 编辑
to 衰人,

代理模式的用意之一就是要能够替代(代理)一个对象,那么如果不从这个对象继承下来,如何代理它呢?我明白你说的意思,你说的是以实现一个行为接口,通过DIP设计理念达到实现接口而不必直接继承具体类来实现代理。
2005-04-16 16:17:00 | [匿名用户:Cavingdeep]
#re: AOP在.NET中的现实 编辑
怎么我觉得动态代理只是需要被代理类有实现接口就可以生成实现该接口的代理类,不需要派生被代理类,所以也就不存在sealed不sealed的了.
class BeenProxyClass beenProxyClass;//本类需要是支持要AOP的接口.这里假设是interface IA;
IA ia = (IA)xxProxy.GetInstance(beenProxyClass);//这里只需要把被代理类作为参数传入,就动态生成了一个beenproxyclassproxy的动态类,而该动态类内部包含了xxproxy类的invoke接口引用,在调用动态类的任何接口任何方法的时候都先调用xxproxy类的invoke函数,然后由invoke函数自动转到实际beenproxyclass的函数上.这里好象没有从beenproxyclass类派生什么.
xxproxy类只需要从包含invoke函数的接口派生.!
2005-04-12 09:20:00 | [匿名用户:衰人]
#re: AOP在.NET中的现实 编辑
to Cavingdeep:

你说的不错。不过,事实上方面源代码在编译的时候实际上就是一个java class,没有什么真正的trick.真正的调用是发生在back-end部分。事实上,据我所知,目前所有的实现都是需要instrument byte code.例如,JBoss使用的是codeassist(? 名字可能不对,一个日本人作的),AspectWerkz开始使用的也是一个开源的byte code library,后来可能换为自己实现了。

我个人觉得AspectWerkz的实现是最强的(似乎听说它和aspectJ合并了?),可以定位到某个instance。
我想在.Net下实现只会更加方便,.Net本身就提供了api可以操作中间代码。
2005-03-30 11:24:00 | [匿名用户:noname]
#re: AOP在.NET中的现实 编辑
使用编译前替换?那么只能static binding,oo的benefits也就享受不到多少了。
to SmartGuy:
你确信aspectJ是这么实现的吗?从表现形式看,aspectJ是使用了语言扩展的形式。不过,从实现上,应当还是instrument compiled byte code.你可以看看,aspectJ应当包含了BECL library---这是一个对于byte code 操作的库。
我理解楼主的意思似乎应当用一个code generator来实现,不知是不是这样?
2005-03-29 17:51:00 | [匿名用户:noname]
#re: AOP在.NET中的现实 编辑
我记得好像是在2002年,有一个工具是编译前或者后替换的。

我记不清是前还是后了。但是,我大致记得的先需要编写一个xml文件,在xml文件中标明需要wear的类的方法,最后编译时指定编写好的xml文件,就完成了。

当时还不明白什么是AOP :-P 。只看了一下它给出的一个Hello World 的例子后就不继续了。
2005-03-29 14:20:00 | [匿名用户:iaxes]
#re: AOP在.NET中的现实 编辑
"个人觉得一种较好的AOP织入实现方式是代码在编译前的替换,这样可以避免所有限制与问题,但缺点是非常难实现。"
为什么在.Net 中这种方式难实现?
AspectJ 中就是用这种方式实现的。为此添加了几个关键字。
2005-03-29 12:42:00 | [匿名用户:SmartGuy]
#re: AOP在.NET中的现实 编辑
to hbifts,

你说的对,不过只是针对我谈到的第一个问题。况且,在真正的OO设计时,我不会首先为Aspect#考虑,有的时候我设计中某些类就必须是sealed,而且不能有接口,比如在做一个“真正的单件”的时候。

AOP的目的之一,就是为了方便我在已发布的程序中扩展业务层面,如果还要为了它而预先破坏我的设计那么我干吗还要用它呢?
2005-03-29 09:29:00 | [匿名用户:Cavingdeep]
#re: AOP在.NET中的现实 编辑
只要你在设置类的时候做到针对Interface编程,那么你提到上述限制基本上就可以避免。。。
2005-03-29 08:48:00 | [匿名用户:hbifts]
#re: AOP在.NET中的现实 编辑
AspectDNG我也看过,它是将程序集反编译后导出成它自己的XML描述IL语言,称之为“IMIL”,然后通过XSLT在这个XML中声明方面的织入,最后将IMIL转成IL再重新编译。它自己的推荐理由是不需要考虑用哪种.NET语言来写实现,只要最终有IL即可。

不过我的意见是:不要用AspectDNG,它的织入机制有问题,像它这种折腾来折腾去的,最终重新编译的程序集我是信不过的,并且它好像并不能处理程序集强签名的情况。而且它的方面声明语言是用XML,定位是用XSLT,使用起来非常不方便(XSLT的限制很大)。另外它目前能实现的功能也是非常的少,总之我个人是不太看好它的,我所看好的是在编译前进行织入的机制!^_^
2005-03-29 08:35:00 | [匿名用户:Cavingdeep]
对不起,目前本随笔不允许发表新评论.

Powered by: Joycode MVC Blogger System