分享
 
 
 

用Delphi实现动态代理(2):设计说明

王朝delphi·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

用Delphi实现动态代理(2):设计说明

[Mental Studio]猛禽[Blog]

在上篇《用Delphi实现动态代理(1):概述》中,对动态代理作了一个概要的说明,比如为什么需要这样的动态代理,它有什么用等。本篇将对我实现的这个动态代理的设计思路作一下介绍。

一、设计目标

如上篇中这幅动态代理结构图所示:

大致的设计目标有以下几项:

TMDynamicProxy可以将任意接口代理到一个通用接口IMInvocationHandler上;

IMInvocationHandler的实现不能太复杂,即TMMethodInvocation的定义要尽量简单;

IMInvocationHandler要能够实现Remoting,即TMMethodInvocation必须可序列化;

需要一个IMMethodInterceptor接口,以便于实现AOP所需要的各种拦截器;

需要一个TMInterfaceInvoker来把IMInvocationHandler转为正确的对象调用。

从上面列出的目标可以看出,我的目标是要实现一套全新的多层框架,并且几乎是对JAVA世界里最流行的轻量容器的模仿。接下来就说明一下原因所在。

二、原因

我之所以对这个动态代理如此热情,源自于对DELPHI下多层技术的愤怒。想想从前在DELPHI开发多层应用有些什么?MIDAS?不可否认,MIDAS是一项很优秀的开发技术,可以在很大程度上简化多层应用的开发。但是正因为它的简单化,所以它跟RAD一样,容易让人在简单化中迷失,而看不到问题的本质--对于这种事情,我喜欢引用老郑的一个经典比喻:就像比基尼泳装,它展示了令人感兴趣的部分,但却隐藏了关键的部分。这导致的结果就是开发出大量的垃圾多层应用--至少跟大部分JAVA或CORBA多层应用相比是这样的。

虽然李维写了《Delphi 5.x多层分布式应用》系列三本书,试图深入地解析一下MIDAS,但是一方面是在这浮躁的氛围下大多数人没有心思深入研究,另一方面则是MIDAS的先天不足。

上图是我在2001年写的一份书稿[1]里的一幅插图,基本上可以说明MIDAS的整体结构,从图上可以看出MIDAS没有自己的基础技术。

其中DCOM、Socket/TCP、Web/HTTP三种连接方式,本质上还是通过COM技术实现的。Socket和Web连接是借助于ScktSrvr和HttpSrvr两个代理程序在服务端与客户端之间建立一个Tunnel来绕过Windows的安全机制或是Firewall。在这样的应用中,Remoting是靠DCOM的ORPC实现,Transactional Data Module服务端的事务处理、Pooling则是交由MTS/COM+ Container实现,而如果是用Remote Data Module/DCOM的话,则得不到这些方面的支持。

那么用CORBA连接的MIDAS呢?在后来版本的DELPHI中--大约是D5或D6开始--取消了这一支持。如果我没记错的话,在D4中的CORBA/MIDAS实现也是基于COM技术的。

最值得一提的是D6开始的SOAP支持,在这里,DELPHI终于提供了一个自己的Remoting实现,也是本文所要讨论的动态代理。不过遗憾的是,这里用的动态代理并没有被做成通用的东西,而是专门为SOAP实现定制了一个,与DELPHI的SOAP实现紧密捆绑。

正因为以上所说的原因,当Kylix出现时,我们面临着在非Windows平台下没有MIDAS的困境。虽然这并非绝境,至少还可以用SOAP/MIDAS,或是纯正的CORBA。遗憾的是SOAP的性能太差,而CORBA--且不说OMG没有提供IDL2PAS的标准--还面临着复杂性的问题。

三、为什么要模仿JAVA

我选择JAVA作为模仿对象的一个原因是我认为:DELPHI需要一个像RMI这样的Remoting技术。

目前流行的Remoting技术有两类:

一是类似于CORBA/DCOM这样的跨语言技术,这样技术需要使用“代码生成”--比如CORBA,需要先写一个定义远程对象的IDL文件,然后通过ORB提供的IDL编译器来生成具体语言的Stub/Skeleton代码;DCOM也类似,需要先写一个MIDL(DELPHI中使用TypeLib Editor实现可视化编辑)来定义服务端对象,生成服务端接口代码,编译服务端程序时,MIDL也被编译成TypeLib并以资源形式链接到服务端程序里;

另一种是类似于RMI这样的独有技术,Remoting的实现依赖于语言本身的动态特性或由平台提供(如.net remoting)。比如RMI,将接口实现从RemoteObject派生即可为接口提供远程访问的语义。

显然,RMI虽然牺牲了跨语言的优势(其实RMI可以通过RMI over IIOP实现与CORBA的互操作,并没有完全牺牲这一优势),但同时也避免了“代码生成”,这是一个很大的好处。

对于“代码生成”这一恶行,我是深有体会的。去年我在试图开发一个基于XML的WEB框架时,一直是用DELPHI提供的XML Data binding来做的。基本的做法是:先用工具(如XMLSPY)做好一个XML Schema(XSD),然后用XML Data binding wizard生成DELPHI的接口和类。当然,一旦生成就可以很方便地使用了,只要在程序里操作这些接口就好了,其中各个Field都会被变成属性,并且类型也都如XSD中的定义。但问题在于程序在开发过程中,总是会有一些变化的,在这种情况下,就不得不同时开着XMLSPY修改XSD,然后重新用XML Data binding的Wizard跑一遍,非常的麻烦。做过CORBA开发的人大概也会有类似这样的体会。虽然在DELPH下做COM开发很方便,有一个可视化的TypeLib Editor可以用,但是还是不爽。

当然,如果是在“理想”的“软件蓝领”环境里,那大概要幸福得多,因为交到Coder手里的接口已经是基本上定死,照着文档做就是了,没有这样的烦恼。但对于Agiler们,这样的麻烦是不可忍受的。

模仿JAVA的另一个原因是我对非侵入的轻量级容器的向往。所谓轻量级和重量级,是相对而言的,评价的标准就是系统对用户代码的侵入性大小。

先来看看一个侵入性强的例子。比如一个典型的MTS/COM+应用,用户代码除了要实现自己定义的业务逻辑接口以外,还至少必须要实现IObjectControl接口。另外,还必须通过ObjectContext与容器进行互动,比如通过IObjectContext接口进行基本的事务和安全性操作;如果要更进一步操作事务,还要通过ITransactionContext;要进行更多安全性操作,还要通过ISecurityProperty接口等。

可以看到,像事务处理、安全性检查这些都是属于基础设施的部分,与用户的业务逻辑没有一点关系,但是这些部分又是“横切”于用户系统结构,与用户代码紧密交织在一起。而且更重要的是这些的控制权都掌握在容器框架手上,用户只能按框架的要求来做,比如MTS/COM+的安全性控制就是与Windows的域用户管理机制紧密捆绑。这带来很大的学习成本,低的灵活性,以及开发的复杂性。

为此,专家们提出了AOP的思想。AOP即Aspect Oriented Programming,面向方面编程。但AOP并不是一种要取代OOP的新技术,而是对OOP处理“横切”问题的一个补充。经典的OOP处理“横切”问题通常是采用Template Method模式或类似的方法来解决,结果就是导致了强侵入性的框架。

基于AOP的思路,非侵入的框架应运而生。

一个典型的非侵入的框架中,用户代码始终是像下面这么简洁明了:

// 业务逻辑定义

{$M+}

IBizIntf = Interface

['{3A85E46D-F3D4-4D9C-A06C-4E7C1BAC9361}']

// 定义业务逻辑

End;

{$M-}

// 服务端业务逻辑实现

TBizImpl = class(TInterfacedObject, IBizIntf)

Protected

// 实现业务逻辑

end;

// 客户端调用代码

Var

BizObj : IBizIntf;

BizObj := TMDynamicProxy( TypeInfo( IBizIntf ), RemoteHandler ) As IBizIntf;

BizObj.xxx // 直接操作服务端对象

在这样的应用中,业务逻辑定义是用DELPHI原生的接口,而不需要另外的像IDL或TypeLib之类,这样整个开发过程都可以在DELPHI语言的范围,用户代码的开发也可以将注意力完全集中在业务逻辑的处理上。

但光有AOP的思想是不够的,还需要有实现。最初的实现是像AspectJ,在JAVA代码编译前作一次预编译,将Aspect代码织入后再用JAVA编译器编译成ByteCode。遗憾的是,这也是一种“代码生成”技术。后来一个叫Jon Tirsen的人利用动态代理设计了一种运行时织入Aspect的实现--Naning[2]。

使用这种动态织入的Interceptor技术,框架提供的附加服务都可以通过Interceptor进行切入。不论是Remoting、事务、安全性,都可以做成相应的Interceptor,然后通过配置或代码在运行时动态地将所需的Interceptor织入来实现所需要的功能。

而这一切的核心,还是“动态代理”。

参考文献

[1]此书已经夭折,引用的插图出自已经完成的第四章,在这里下载

[2]透明《动态代理的前世今生》(《程序员》2005年第1期)

[Mental Studio]猛禽 Feb.28-05, Mar.01

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有