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