動(dòng)態(tài)代理機(jī)制初探
功能代碼的多余枝節(jié)
當(dāng)我們書(shū)寫(xiě)執(zhí)行一個(gè)功能的函數(shù)時(shí),經(jīng)常需要在其中寫(xiě)入與功能不是直接相關(guān)但很有必要的代碼,如日志記錄,信息發(fā)送,安全和事務(wù)支持等,以下代碼是一個(gè)用戶注冊(cè)類的代碼:






















Logger類代碼


























MailSender類代碼

















枝節(jié)性代碼給功能性代碼帶來(lái)的麻煩
諸如日志記錄,信息發(fā)送,安全和事務(wù)支持等枝節(jié)代碼雖然是必要的,但它會(huì)帶來(lái)以下麻煩:
1.枝節(jié)性代碼游離在功能性代碼之外,它們不是函數(shù)的目的,這對(duì)OO是一種破壞。
2.枝節(jié)性代碼會(huì)造成功能性代碼對(duì)其它類的依賴,加深類之間的耦合度,而這是OO系統(tǒng)所竭力避免的。
3.枝節(jié)性代碼帶來(lái)的耦合度會(huì)造成功能性代碼移植困難,可重用性降低。
4.從法理上說(shuō),枝節(jié)性代碼應(yīng)該“監(jiān)視”著功能性代碼,然后采取行動(dòng);而不是由功能性代碼“通知”枝節(jié)性代碼采取行動(dòng)。這好比吟游詩(shī)人應(yīng)該是主動(dòng)記述騎士的功績(jī)而不是騎士主動(dòng)要求詩(shī)人記錄自己的功績(jī)的。
如何兩種代碼分離開(kāi)來(lái)
毫無(wú)疑問(wèn),枝節(jié)性代碼和功能性代碼(主干性代碼)需要分離開(kāi)來(lái)才能降低耦合程度,符合現(xiàn)代OO系統(tǒng)的要求,而java提供的動(dòng)態(tài)代理機(jī)制可以幫助我們實(shí)現(xiàn)這一點(diǎn)。
動(dòng)態(tài)代理機(jī)制主要的類是java.lang.reflect.Proxy,它從一誕生就受到了重視,并在RMI,EJB和AOP中都得到廣泛的應(yīng)用,其重要程度唯有反射能與之相比。
Proxy代理模式
在講述動(dòng)態(tài)代理之前我們可以回顧一下代理模式,它的定義是這樣的:代理可以提供對(duì)另一個(gè)對(duì)象的訪問(wèn),同時(shí)隱藏實(shí)際對(duì)象的具體事實(shí)。代理一般會(huì)實(shí)現(xiàn)它所表示的實(shí)際對(duì)象的接口。代理可以訪問(wèn)實(shí)際對(duì)象,但是延遲實(shí)現(xiàn)實(shí)際對(duì)象的部分功能,實(shí)際對(duì)象實(shí)現(xiàn)系統(tǒng)的實(shí)際功能,代理對(duì)象對(duì)客戶隱藏了實(shí)際對(duì)象。客戶不知道它是與代理打交道還是與實(shí)際對(duì)象打交道。
如果我們使用代理模式,把枝節(jié)性代碼放入代理類中,這樣主干性代碼保持在真實(shí)的類中,這樣不就能有效降低耦合度嗎?這種通過(guò)在耦合緊密的類之間引入一個(gè)中間類是降低類之間的耦合度的常見(jiàn)做法。
具體來(lái)說(shuō)就是把枝節(jié)性代碼放入代理類中,它們由代理類負(fù)責(zé)調(diào)用,而真實(shí)類只負(fù)責(zé)主干的核心業(yè)務(wù),它也由代理類調(diào)用,它并不知道枝節(jié)性代碼的存在和作用,因?yàn)檫@本不是它的任務(wù)。對(duì)外來(lái)說(shuō),代理類隱藏在接口之后,客戶并不清楚也不需要清楚具體的調(diào)用過(guò)程。通過(guò)這樣的處理,主干與枝節(jié)之間的交叉解開(kāi)了,外界的調(diào)用也沒(méi)有復(fù)雜化,這就有效降低系統(tǒng)各部分間的耦合度。
下面讓我們先看看代碼
消除了枝節(jié)代碼的注冊(cè)類



















注冊(cè)類的代理類,枝節(jié)性代碼都被轉(zhuǎn)移到了這里









































代理類RegisterServiceProxy的解釋
該代理類的內(nèi)部屬性為Object類,實(shí)際使用時(shí)通過(guò)該類的構(gòu)造函數(shù)RegisterServiceProxy(Object obj)對(duì)其賦值;此外,在該類還實(shí)現(xiàn)了invoke方法,該方法中的
method.invoke(obj,args);
其實(shí)就是調(diào)用被代理對(duì)象的將要被執(zhí)行的方法,這是通過(guò)反射實(shí)現(xiàn)的,方法參數(shù)obj是實(shí)際的被代理對(duì)象,args為執(zhí)行被代理對(duì)象相應(yīng)操作所需的參數(shù)。通過(guò)動(dòng)態(tài)代理類,我們可以在調(diào)用之前或者之后執(zhí)行一些相關(guān)操作。
如何生成一個(gè)代理類的實(shí)例
代理類的實(shí)例需要特殊的方式生成,代碼如下:






Proxy即為java中的動(dòng)態(tài)代理類,其方法Static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理類的一個(gè)實(shí)例,其中l(wèi)oader是類加載器,interfaces是被代理的真實(shí)類的接口,h是具體的代理類實(shí)例。
所謂動(dòng)態(tài)代理是這樣一種class:它是在運(yùn)行時(shí)生成的類,在生成它時(shí)你必須提供一組接口給它,然后該類就宣稱它實(shí)現(xiàn)了這些接口。你當(dāng)然可以把該類的實(shí)例當(dāng)作這些接口中的任何一個(gè)實(shí)現(xiàn)類來(lái)用。當(dāng)然啦,這個(gè)動(dòng)態(tài)代理類其實(shí)就是一個(gè)代理,它不會(huì)做作實(shí)質(zhì)性的工作,而是在生成它的實(shí)例時(shí)你必須提供一個(gè)真實(shí)的類的實(shí)例,由它接管實(shí)際的工作。
工廠方法的作用
對(duì)于代理類生成的細(xì)節(jié),客戶(需要使用RegisterService的程序員)是沒(méi)有興趣也沒(méi)有必要知道的,我們可以讓它隱藏在一個(gè)工廠方法中,對(duì)外返回一個(gè)接口,這樣在調(diào)用時(shí)用戶就不知道他是與代理打交道還是與實(shí)際對(duì)象打交道了。使用RegisterService類時(shí)示例代碼如下:


執(zhí)行完的結(jié)果和前面的代碼的是一樣的。
動(dòng)態(tài)代理在AOP中的應(yīng)用
Spring的AOP支持可以被用于從系統(tǒng)核心邏輯中分離交叉業(yè)務(wù)(cross-business)如日志,事務(wù)管理和安全等,使用AOP,你可以用各種功能層來(lái)覆蓋核心業(yè)務(wù)層,這些功能層可以靈活的應(yīng)用到你的系統(tǒng)中,甚至核心業(yè)務(wù)層都不知道它們的存在,這是一個(gè)強(qiáng)大的概念。
AOP(aspect-oriented programming)的核心就是動(dòng)態(tài)代理,掌握它對(duì)于理解AOP尤為重要,猶如反射對(duì)理解IoC一樣。
代碼下載:
http://www.aygfsteel.com/Files/sitinspring/DynamicProxySample20080527235441.rar
posted on 2008-05-28 00:06 sitinspring 閱讀(2368) 評(píng)論(3) 編輯 收藏 所屬分類: SSH