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