I坐标

博客园 首页 新随笔 联系 订阅 管理
Summary        这篇文章介绍了Castle的动态代理是如何实现 AOP 的。

        向来我是“拿来主义”,即只管拿来用,不管正在用的东西是怎么实现的。最近由于一直想把 AOP 以及 IoC 等技术加入到项目中,因此对这些技术相当关注。后来选择了CastleProject中的DynamicProxy作为关注对象。不过这次起了贪心,不想再只知道使用,不知道如何实现了,于是开始深入去查看Castle是如何实现 AOP 的。


        Proxy 是实现 AOP 的途径之一,通过代理可以有效的拦截某个类方法的执行过程。


        从Castle中的测试例子可以看出,Castle使用了动态代码生成来对需要拦截的类生成一个代理,从而达到织入的目的。但是Castle是如何织入的呢?我打算使用一个简单的例子来测试Castle动态生成的代码。


Scene        我设想了一个例子,一个类有一个方法:DoSomething,这个方法调用时输出Yes, * is running DoSomething,其中 * 表示类的名称。然后我需要在这个类调用DoSomething之前及之后,还需要输出Now i'm starting DoSomething以及 Now i'm ending DoSomething。当然,有很多方法可以实现,由于Castle是使用代理,所以我这里只使用代理。

        对于传统的代理模式,可以得出如下的类图:


图1




        上图的类的代码分别如下:


图2:类A的代码




图3:类RealANoVirtual的代码




图4:类ProxyClass的代码



        然后,在使用这些时,可以如下编写代码:


图5:运行代码




        运行上面的代码,我们就可以特出如下的结果:


图6:运行结果




        通过代理,可以透明的使用DoSomething方法。


        那么很显然,Castle也需要生成这样一个代理类,从而能够在调用DoSomething之前调用用户指定的代码。因此Castle提供了DynamicProxy以及Interceptor来达到目的。其中DynamicProxy会生成一个代理类,代理DoSomething操作,而在代理DoSomething操作时,使用用户指定的Interceptor达到拦截操作的目的。


        为了看到Castle生成的动态代理是怎么样的,我写了一个类:RealA,代码如下:


图7:RealA代码




        RealA中只有一个方法,DoSomething。我需要在DoSomething操作前后达到与前面的传统的模式一样的效果,按照Castle的要求,我需要写一个Interceptor,从而能够拦截前后的操作,代码如下:


图8:Interceptor代码




        暂时不去考虑ProxyInterceptor是怎么一回事,继续往下。


        有了需要拦截的类与Interceptor,那么直接就可以使用了,如下的代码:


图9:运行代码




图10:运行的结果




        Bingle!!!,成功了。


        那么Castle究竟做了什么呢?


        在运行时,Castle会生成一个Assembly,放在应用的运行目录下,名称为:GeneratedAssembly.dll。通过反编译这个Assembly,就可以看到Castle究竟做了什么。


        为了更好的说明,我把反编译后的代码分段说明:


1、Castle生成了一个代理类CProxyTypeRealA0,代理类继承自RealA:


public class CProxyTypeRealA0 : RealA


{


...


}


2、在CProxyTypeRealA0类中如下定义了一些Field:


图11:Field定义




        其中的delegate的定义如下:


图12:delegate的定义




        那么,这些Delegate的目的是什么呢?通过CProxyTypeRealA0的构造函数中的代码可以看出用途:


图13:构造函数




        这里,我们关注的是DoSomething。从上面的构造函数代码可以看出,delegate指向了callback__DoSomething这个回调函数,这个函数的代码如下:


图14:回调函数代码




        在这个回调函数中,就直接调用了RealA的DoSomething方法。


        那么,Castle是如何调用到RealA的DoSomething的方法的呢?


3、调用链


        在CProxyTypeRealA0中,覆盖了RealA的DoSomething方法,如下:


图15:覆盖的DoSomething




        从代码中可以看出,DoSomething中调用了本地方法_Method2Invocation获取一个Invocation,然后调用Interceptor执行一些动作。


        仔细看,在_Method2Invocation调用时传入了一个参数this.cached1,这个参数是一个delegate对象,并且在构造函数中指向了回调函数callback_DoSomething。这是一个很关键的地方,因此我们有必要看看_Method2Invocation作了什么。


图16 Method2Invocation的代码




        Method2Invocation方法中,创建/获取了一个MethodInfo对应的Invocation,而Invocation封装了传入的delegate等相关信息。


        很显然,封装这些信息是为了更好的传递。从Method2Invocation出来,接下去就是调用在构造函数中传入的Interceptor。


        Interceptor的Intercept方法需要两个参数,一个就是前面封装好的Invocation对象,另一个是一个参数数组。目前看不出这个参数数组有什么用。


        那么Intercept方法作了什么


        看看Interceptor的实现代码,我的例子中的Interceptor直接继承自StandardInterceptor,所以先看看StandardInterceptor的代码:


图17 StandardInterceptor的代码




        从代码中可以看出,这是一个模板模式。分别调用了PreProcess,PostProcess方法。这样就可以调用了我覆盖的两个方法。但是,在这里依然没有看到调用了DoSomething方法,别急,代码中调用了传入的Invocaion对象的Process方法。那么这个方法作了什么?


        由于在Method2Invocation中,创建的是SameClassInvocation对象,因此,直接看SameClassInvocation的代码:


图18 SameClassInvocation的代码




        Bingle!!! Process方法调用了传入的delegate对象,这样就调用到了回调函数callback_DoSomething,而回调函数又调用了RealA的DoSomething方法。这样,一个完整的拦截过程就实现了。同时,这里也看到前面的参数数组就是DoSomething的参数数组,只不过例子中没有参数,所以为零了。


        至此,我们看到了Castle完成代码织入的整个过程:


        首先通过代码生成完成一个代理类,该代理类继承自要织入的类。然后在代理类中覆盖要拦截的方法,并在覆盖的方法中封装Invocation对象,并传给用户传入的Intercepter对象的Intercept方法。在Intercept方法依次调用Intercepter的PreProcess,通过Invocation传入的Delegate指向的回调函数,Intercepter的PostProcess方法,从而达到拦截的目的。


下面是例子的代码下载:


例子下载


示例在Snippet Comiler 2.0 中编译运行通过。编译时需要自行添加对Castle.DynamicProxy.dll的引用。


如果在VS.NET中运行,需要自己创建Project,并加入压缩包中的文件。


posted on 2005-04-07 09:33  I坐标  阅读(8523)  评论(7编辑  收藏  举报