惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
有赞技术团队
有赞技术团队
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
D
DataBreaches.Net
F
Full Disclosure
腾讯CDC
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
Stack Overflow Blog
Stack Overflow Blog
小众软件
小众软件
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
大猫的无限游戏
大猫的无限游戏
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
S
SegmentFault 最新的问题
The Register - Security
The Register - Security
WordPress大学
WordPress大学
博客园 - 聂微东
雷峰网
雷峰网
J
Java Code Geeks
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Privacy International News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
A
Arctic Wolf
Scott Helme
Scott Helme
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Tor Project blog
博客园 - 三生石上(FineUI控件)
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CERT Recently Published Vulnerability Notes
O
OpenAI News
Project Zero
Project Zero
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Application and Cybersecurity Blog
Application and Cybersecurity Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Microsoft Security Blog
Microsoft Security Blog
Cisco Talos Blog
Cisco Talos Blog
P
Palo Alto Networks Blog
Schneier on Security
Schneier on Security

博客园 - I坐标

重新开源UDS Google Wave 提名邀请发送 时尚的轮回-Ajax Castle与Mixin Catalysis的建模的3个层次 Catalysis 的构成部分与框架 使用log4net将日志记入PostgreSql Log4Net Appender 之 ADONetAppender 谁对谁失去了信心? Castle与Spring.Net在用法和配置上的区别 - I坐标 - 博客园 ICE:Slice语言(六)--接口、操作和异常(一) ICE:Slice语言(五)--用户定义类型 在ClickOnce和Windows Installer之间选择 合并XML文件与在客户端管理Web服务引用 ICE:Slice语言(四)--基本类型 ICE:Slice语言(三)--模块 ICE:Slice语言(二)--源文件和词法规则 ICE:Slice语言(一)-编译 ICE:5个服务
用动态代理实现AOP
I坐标 · 2005-04-07 · via 博客园 - 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,并加入压缩包中的文件。