Java的三種代理模式
參考:http://www.cnblogs.com/cenyu/...
Java核心技術(shù)原書(shū)第九版6.5節(jié)
我們?cè)趯?xiě)一個(gè)功能函數(shù)時(shí),經(jīng)常需要在其中寫(xiě)入與功能不是直接相關(guān)但很有必要的代 碼,如日志記錄,信息發(fā)送,安全和事務(wù)支持等,這些枝節(jié)性代碼雖然是必要的,但它會(huì)帶來(lái)以下麻煩:
枝節(jié)性代碼游離在功能性代碼之外,它不是函數(shù)的目的,這是對(duì)OO是一種破壞
枝節(jié)性代碼會(huì)造成功能性代碼對(duì)其它類(lèi)的依賴(lài),加深類(lèi)之間的耦合,可重用性降低
從法理上說(shuō),枝節(jié)性代碼應(yīng)該監(jiān)視"著功能性代碼,然后采取行動(dòng),而不是功能性代碼 通知"枝節(jié)性代碼采取行動(dòng),這好比吟游詩(shī)人應(yīng)該是主動(dòng)記錄騎士的功績(jī)而不是騎士主動(dòng)要求詩(shī)人記錄自己的功績(jī)
常見(jiàn)的代理有:遠(yuǎn)程代理(Remote Proxy):對(duì)一個(gè)位于不同的地址空間對(duì)象提供一個(gè)局域代表對(duì)象,如RMI中的stub
虛擬代理(Virtual Proxy):根據(jù)需要將一個(gè)資源消耗很大或者比較復(fù)雜的對(duì)象,延遲加 載,在真正需要的時(shí)候才創(chuàng)建
保護(hù)代理(Protect or Access Proxy):控制對(duì)一個(gè)對(duì)象的訪(fǎng)問(wèn)權(quán)限。
智能引用(Smart Reference Proxy):提供比目標(biāo)對(duì)象額外的服務(wù)和功能。
定義 代理(Proxy)是一種設(shè)計(jì)模式,定義:為其他對(duì)象提供一個(gè)代理以控制對(duì)某個(gè)對(duì)象的訪(fǎng)問(wèn),即通過(guò)代理對(duì)象訪(fǎng)問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫(xiě)好的代碼或者方法,如果需改修改,可以通過(guò)代理的方式來(lái)擴(kuò)展該方法

代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象
靜態(tài)代理在使用時(shí),需要定義接口或者父類(lèi),被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類(lèi).
關(guān)鍵:在編譯期確定代理對(duì)象,在程序運(yùn)行前代理類(lèi)的.class文件就已經(jīng)存在了。
比如:在代理對(duì)象中實(shí)例化被代理對(duì)象或者將被代理對(duì)象傳入代理對(duì)象的構(gòu)造方法
例子:
模擬保存動(dòng)作,定義一個(gè)保存動(dòng)作的接口:IUserDao.java,然后目標(biāo)對(duì)象UserDao.java實(shí)現(xiàn)這個(gè)接口的方法,此時(shí)如果使用靜態(tài)代理方式,就需要在代理對(duì)象(UserDaoProxy.java)中也實(shí)現(xiàn)IUserDao接口.調(diào)用的時(shí)候通過(guò)調(diào)用代理對(duì)象的方法來(lái)調(diào)用目標(biāo)對(duì)象.
需要注意的是,代理對(duì)象與目標(biāo)對(duì)象要實(shí)現(xiàn)相同的接口,然后通過(guò)調(diào)用相同的方法來(lái)調(diào)用目標(biāo)對(duì)象的方法
接口:IUserDao.java
public interface IUserDao { void save(); }
目標(biāo)對(duì)象類(lèi):UserDao.java
public class UserDao implements IUserDao { public void save() { System.out.println("----已經(jīng)保存數(shù)據(jù)!----"); } }
代理對(duì)象:UserDaoProxy.java
public class UserDaoProxy implements IUserDao{ //接收保存目標(biāo)對(duì)象 private IUserDao target; public UserDaoProxy(IUserDao target){ this.target=target; } public void save() { System.out.println("開(kāi)始事務(wù)..."); target.save();//執(zhí)行目標(biāo)對(duì)象的方法 System.out.println("提交事務(wù)..."); } }
測(cè)試類(lèi):App.java
public class App { public static void main(String[] args) { //目標(biāo)對(duì)象 UserDao target = new UserDao(); //代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系 UserDaoProxy proxy = new UserDaoProxy(target); proxy.save();//執(zhí)行的是代理的方法 } }靜態(tài)代理總結(jié):
可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展.
缺點(diǎn):
代理類(lèi)和委托類(lèi)實(shí)現(xiàn)相同的接口,同時(shí)要實(shí)現(xiàn)相同的方法。這樣就出現(xiàn)了大量的代碼重復(fù)。如果接口增加一個(gè)方法,除了所有實(shí)現(xiàn)類(lèi)需要實(shí)現(xiàn)這個(gè)方法外,所有代理類(lèi)也需要實(shí)現(xiàn)此方法。增加了代碼維護(hù)的復(fù)雜度。
在運(yùn)行期,通過(guò)反射機(jī)制創(chuàng)建一個(gè)實(shí)現(xiàn)了一組給定接口的新類(lèi)
在運(yùn)行時(shí)生成的class,必須提供一組interface給它,然后該class就宣稱(chēng)它實(shí)現(xiàn)了這些 interface。該class的實(shí) 例可以當(dāng)作這些interface中的任何一個(gè)來(lái)用。但是這個(gè)Dynamic Proxy其實(shí)就是一個(gè)Proxy, 它不會(huì)替你作實(shí)質(zhì)性的工作,在生成它的實(shí)例時(shí)你必須提供一個(gè)handler,由它接管實(shí)際的工 作。
動(dòng)態(tài)代理也叫做:JDK代理,接口代理
接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個(gè)集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數(shù)量比較多的時(shí)候,我們可以進(jìn)行靈活處理,而不需要像靜態(tài)代理那樣每一個(gè)方法進(jìn)行中轉(zhuǎn)。而且動(dòng)態(tài)代理的應(yīng)用使我們的類(lèi)職責(zé)更加單一,復(fù)用性更強(qiáng)
JDK中生成代理對(duì)象的API
代理類(lèi)所在包:java.lang.reflect.Proxy
JDK實(shí)現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù),完整的寫(xiě)法是:
static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)
注意該方法是在Proxy類(lèi)中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:
ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用類(lèi)加載器,用null表示默認(rèn)類(lèi)加載器
Class [] interfaces:需要實(shí)現(xiàn)的接口數(shù)組
InvocationHandler handler:調(diào)用處理器,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)調(diào)用處理器的方法,從而把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
java.lang.reflect.InvocationHandler:這是調(diào)用處理器接口,它自定義了一個(gè) invoke 方法,用于集中處理在動(dòng)態(tài)代理類(lèi)對(duì)象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對(duì)委托類(lèi)的代理訪(fǎng)問(wèn)。
// 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類(lèi)上的所有方法調(diào)用。第一個(gè)參數(shù)既是代理類(lèi)實(shí)例,第二個(gè)參數(shù)是被調(diào)用的方法對(duì)象 // 第三個(gè)方法是調(diào)用參數(shù)。 Object invoke(Object proxy, Method method, Object[] args)
代碼示例:
接口類(lèi)IUserDao.java以及接口實(shí)現(xiàn)類(lèi)UserDao是一樣的.在這個(gè)基礎(chǔ)上,增加一個(gè)代理工廠(chǎng)類(lèi)(ProxyFactory.java),將代理類(lèi)寫(xiě)在這個(gè)地方,然后在測(cè)試類(lèi)中先建立目標(biāo)對(duì)象和代理對(duì)象的聯(lián)系,然后使用代理對(duì)象中的同名方法
代理工廠(chǎng)類(lèi):ProxyFactory.java
/** * 創(chuàng)建動(dòng)態(tài)代理對(duì)象 * 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類(lèi)型 */ public class ProxyFactory{ //維護(hù)一個(gè)目標(biāo)對(duì)象 private Object target; public ProxyFactory(Object target){ this.target=target; } //給目標(biāo)對(duì)象生成代理對(duì)象 public Object getProxyInstance(){ return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("開(kāi)始事務(wù)2"); //執(zhí)行目標(biāo)對(duì)象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務(wù)2"); return returnValue; } } ); } }
測(cè)試類(lèi):App.java
/** * 測(cè)試類(lèi) */ public class App { public static void main(String[] args) { // 目標(biāo)對(duì)象 IUserDao target = new UserDao(); // 【原始的類(lèi)型 class cn.itcast.b_dynamic.UserDao】 System.out.println(target.getClass()); // 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); // class $Proxy0 內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象 System.out.println(proxy.getClass()); // 執(zhí)行方法 【代理對(duì)象】 proxy.save(); } }
總結(jié):
代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理
上面的靜態(tài)代理和動(dòng)態(tài)代理模式都是要求目標(biāo)對(duì)象實(shí)現(xiàn)一個(gè)接口或者多個(gè)接口,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)多帶帶的對(duì)象,并沒(méi)有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用構(gòu)建目標(biāo)對(duì)象子類(lèi)的方式實(shí)現(xiàn)代理,這種方法就叫做:Cglib代理
Cglib代理,也叫作子類(lèi)代理,它是在內(nèi)存中構(gòu)建一個(gè)子類(lèi)對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.
Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類(lèi)與實(shí)現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
Cglib包的底層是通過(guò)使用字節(jié)碼處理框架ASM來(lái)轉(zhuǎn)換字節(jié)碼并生成新的子類(lèi).
代理的類(lèi)不能為final,否則報(bào)錯(cuò);目標(biāo)對(duì)象的方法如果為final/static,那么就不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法.
代碼示例:
目標(biāo)對(duì)象類(lèi):UserDao.java
/** * 目標(biāo)對(duì)象,沒(méi)有實(shí)現(xiàn)任何接口 */ public class UserDao { public void save() { System.out.println("----已經(jīng)保存數(shù)據(jù)!----"); } }
Cglib代理工廠(chǎng):ProxyFactory.java
/** * Cglib子類(lèi)代理工廠(chǎng) * 對(duì)UserDao在內(nèi)存中動(dòng)態(tài)構(gòu)建一個(gè)子類(lèi)對(duì)象 */ public class ProxyFactory implements MethodInterceptor{ //維護(hù)目標(biāo)對(duì)象 private Object target; public ProxyFactory(Object target) { this.target = target; } //給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象 public Object getProxyInstance(){ //1.工具類(lèi) Enhancer en = new Enhancer(); //2.設(shè)置父類(lèi) en.setSuperclass(target.getClass()); //3.設(shè)置回調(diào)函數(shù) en.setCallback(this); //4.創(chuàng)建子類(lèi)(代理對(duì)象) return en.create(); } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("開(kāi)始事務(wù)..."); //執(zhí)行目標(biāo)對(duì)象的方法 Object returnValue = method.invoke(target, args); System.out.println("提交事務(wù)..."); return returnValue; } }
測(cè)試類(lèi):
/** * 測(cè)試類(lèi) */ public class App { @Test public void test(){ //目標(biāo)對(duì)象 UserDao target = new UserDao(); //代理對(duì)象 UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //執(zhí)行代理對(duì)象的方法 proxy.save(); } }
AOP(AspectOrientedProgramming):
將日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來(lái),通過(guò)對(duì)這些行為的分離,我們希望可以將它們獨(dú)立到非業(yè)務(wù)邏輯的方法中,進(jìn)而改變這些行為的時(shí)候不影響業(yè)務(wù)邏輯的代碼---解耦。
**
在Spring的AOP編程中:
如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,用JDK代理
如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,用Cglib代理**
UML類(lèi)圖基本沒(méi)區(qū)別,都是實(shí)現(xiàn)同一個(gè)接口,一個(gè)類(lèi)包裝另一 個(gè)類(lèi)。 兩者的定義:
裝飾器模式:能動(dòng)態(tài)的新增或組合對(duì)象的行為
在不改變接口的前提下,動(dòng)態(tài)擴(kuò)展對(duì)象的功能
代理模式:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)
在不改變接口的前提下,控制對(duì)象的訪(fǎng)問(wèn)
裝飾模式是“新增行為”,而代理模式是“控制訪(fǎng)問(wèn)”。關(guān)鍵就是我們?nèi)绾闻袛嗍恰靶略鲂?為”還是“控制訪(fǎng)問(wèn)”。你在一個(gè)地方寫(xiě)裝飾,大家就知道這是在增加功能,你寫(xiě)代理,大 家就知道是在限制。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69940.html
摘要:代理模式的實(shí)現(xiàn)靜態(tài)代理優(yōu)缺點(diǎn)優(yōu)點(diǎn)只對(duì)對(duì)需要的方法加代理邏輯。通過(guò)繼承的方式進(jìn)行代理,無(wú)論目標(biāo)對(duì)象有沒(méi)有實(shí)現(xiàn)接口都可以代理,但是無(wú)法處理的情況。 注意:本文所有的class使用的static修飾主要是為了能在一個(gè)類(lèi)里面測(cè)試。實(shí)際項(xiàng)目中不應(yīng)該這樣做的,應(yīng)該分包分class。文字描述不是很多,還是看代碼比較好理解吧... 1. Java代理的理解 代理模式是一種設(shè)計(jì)模式,簡(jiǎn)單說(shuō)即是在不改變?cè)?..
摘要:面試題增強(qiáng)一個(gè)對(duì)象的方法的三種方式繼承使用這種方式必須滿(mǎn)足的條件是被增強(qiáng)的方法的所在類(lèi)能被繼承,并且這個(gè)對(duì)象已經(jīng)明確知道。所以創(chuàng)建一個(gè)類(lèi)繼承重寫(xiě)了父類(lèi)的方法增強(qiáng)了,變成飛了。。。 面試題:增強(qiáng)一個(gè)對(duì)象的方法的三種方式 1. 繼承 使用這種方式必須滿(mǎn)足的條件是:被增強(qiáng)的方法的所在類(lèi)能被繼承,并且這個(gè)對(duì)象已經(jīng)明確知道。 舉例: 有一個(gè)接口Person,里面有一個(gè)方法run() pack...
摘要:代理模式代理類(lèi)中創(chuàng)建一個(gè)真實(shí)對(duì)象的實(shí)例模式的核心裝飾者強(qiáng)調(diào)的是增強(qiáng)自身,在被裝飾之后你能夠在被增強(qiáng)的類(lèi)上使用增強(qiáng)后的功能。 代理模式 在詳細(xì)了解代理模式之前,可能對(duì)于像小秋一樣的小白,只知道一些很淺顯的概念,或者就知道遠(yuǎn)程代理啊,靜態(tài)代理啊,動(dòng)態(tài)代理啊,這些看似可以望文生義的專(zhuān)業(yè)名詞,但是如果我告訴你代理模式貫穿了我們生活的方方面面,就比如你現(xiàn)在刷著公眾號(hào)的時(shí)候,實(shí)際上就用了遠(yuǎn)程代理模...
摘要:中怎樣實(shí)現(xiàn)類(lèi)之間的關(guān)系如一對(duì)多多對(duì)多的關(guān)系中怎樣實(shí)現(xiàn)類(lèi)之間的關(guān)系如一對(duì)多多對(duì)多的關(guān)系它們通過(guò)配置文件中的來(lái)實(shí)現(xiàn)類(lèi)之間的關(guān)聯(lián)關(guān)系的。 Hibernate常見(jiàn)面試題 Hibernate工作原理及為什么要用? Hibernate工作原理及為什么要用? 讀取并解析配置文件 讀取并解析映射信息,創(chuàng)建SessionFactory 打開(kāi)Sesssion 創(chuàng)建事務(wù)Transation 持久化操作 提...
摘要:包裝模式是這樣干的首先我們弄一個(gè)裝飾器,它實(shí)現(xiàn)了接口,以組合的方式接收我們的默認(rèn)實(shí)現(xiàn)類(lèi)。其實(shí)裝飾器抽象類(lèi)的作用就是代理核心的功能還是由最簡(jiǎn)單的實(shí)現(xiàn)類(lèi)來(lái)做,只不過(guò)在擴(kuò)展的時(shí)候可以添加一些沒(méi)有的功能而已。 前言 只有光頭才能變強(qiáng) 回顧前面: 給女朋友講解什么是代理模式 前一篇已經(jīng)講解了代理模式了,今天要講解的就是裝飾模式啦~ 在看到FilterInputStream和FilterOutpu...
閱讀 1824·2021-11-15 11:38
閱讀 4754·2021-09-22 15:33
閱讀 2484·2021-08-30 09:46
閱讀 2337·2019-08-30 15:43
閱讀 974·2019-08-30 14:16
閱讀 2226·2019-08-30 13:09
閱讀 1422·2019-08-30 11:25
閱讀 850·2019-08-29 16:42