java 动态代理深度学习(Proxy,InvocationHandler)
http://hi.baidu.com/malecu/item/9e0edc115cb597a1feded5a0
http://www.iteye.com/topic/683613
http://blog.csdn.net/zsmj_2011/article/details/10394805
http://www.ibm.com/developerworks/cn/java/j-lo-hibernatelazy/index.html?ca=dat
通常情況下,適用代理模式的情況有兩種:
1.創建對象開銷很大,可以創建一個代理對象,推遲真正的對象創建。大家所熟悉的Hibernate延遲加載策略就是使用動態代理,當A實體關聯B實體時,在獲取A實體時不需要立即獲得與A實體關聯的B實體,因為有可能客戶端根本不需要B實體數據,當客戶端真正需要這部分數據時,再加載B實體的數據。這樣就節省了資源。
2.所創建的對象不能滿足客戶端需求,比如說我們需要為某個方法運行前加入一些驗證或者過濾,這時我們就需要創建一個代理對象,增強原來對象的功能。
代理模式
代理模式是一種應用非常廣泛的設計模式,當客戶端代碼需要調用某個對象時,客戶端實際上也不關心是否準確得到該對象,它只要一個能提供該功能的對象即可,此時我們就可返回該對象的代理(Proxy)。
在這種設計方式下,系統會為某個對象提供一個代理對象,并由代理對象控制對源對象的引用。代理就是一個 Java 對象代表另一個 Java 對象來采取行動。在某些情況下,客戶端代碼不想或不能夠直接調用被調用者,代理對象可以在客戶和目標對象之間起到中介的作用。
對客戶端而言,它不能分辨出代理對象與真實對象的區別,它也無須分辨代理對象和真實對象的區別。客戶端代碼并不知道真正的被代理對象,客戶端代碼面向接口編程,它僅僅持有一個被代理對象的接口。
總而言之,只要客戶端代碼不能或不想直接訪問被調用對象——這種情況有很多原因,比如需要創建一個系統開銷很大的對象,或者被調用對象在遠程主機上,或者目標對象的功能還不足以滿足需求……,而是額外創建一個代理對象返回給客戶端使用,那么這種設計方式就是代理模式。
下面示范一個簡單的代理模式,程序首先提供了一個 Image 接口,代表大圖片對象所實現的接口,該接口代碼如下:
清單 3. Image.java
public interface Image { void show(); }該接口提供了一個實現類,該實現類模擬了一個大圖片對象,該實現類的構造器使用 Thread.sleep() 方法來暫停 3s。下面是該 BigImage 的程序代碼。
清單 4. BigImage.java
// 使用該 BigImage 模擬一個很大圖片public class BigImage implements Image { public BigImage() { try { // 程序暫停 3s 模式模擬系統開銷 Thread.sleep(3000); System.out.println("圖片裝載成功 ..."); } catch (InterruptedException ex) { ex.printStackTrace(); } } // 實現 Image 里的 show() 方法public void show() { System.out.println("繪制實際的大圖片"); } }上面的程序代碼暫停了 3s,這表明創建一個 BigImage 對象需要 3s 的時間開銷——程序使用這種延遲來模擬裝載此圖片所導致的系統開銷。如果不采用代理模式,當程序中創建 BigImage 時,系統將會產生 3s 的延遲。為了避免這種延遲,程序為 BigImage 對象提供一個代理對象,BigImage 類的代理類如下所示。
清單 5. ImageProxy.java
public class ImageProxy implements Image { // 組合一個 image 實例,作為被代理的對象private Image image; // 使用抽象實體來初始化代理對象public ImageProxy(Image image) { this.image = image; } /** * 重寫 Image 接口的 show() 方法* 該方法用于控制對被代理對象的訪問,* 并根據需要負責創建和刪除被代理對象*/ public void show() { // 只有當真正需要調用 image 的 show 方法時才創建被代理對象if (image == null) { image = new BigImage(); } image.show(); } }上面的 ImageProxy 代理類實現了與 BigImage 相同的 show() 方法,這使得客戶端代碼獲取到該代理對象之后,可以將該代理對象當成 BigImage 來使用。
在 ImageProxy 類的 show() 方法中增加了控制邏輯,這段控制邏輯用于控制當系統真正調用 image 的 show() 時,才會真正創建被代理的 BigImage 對象。下面程序需要使用 BigImage 對象,但程序并不是直接返回 BigImage 實例,而是先返回 BigImage 的代理對象,如下面程序所示。
清單 6. BigImageTest.java
public class BigImageTest { public static void main(String[] args) { long start = System.currentTimeMillis(); // 程序返回一個 Image 對象,該對象只是 BigImage 的代理對象Image image = new ImageProxy(null); System.out.println("系統得到 Image 對象的時間開銷 :" + (System.currentTimeMillis() - start)); // 只有當實際調用 image 代理的 show() 方法時,程序才會真正創建被代理對象。image.show(); } }上面程序初始化 image 非常快,因為程序并未真正創建 BigImage 對象,只是得到了 ImageProxy 代理對象——直到程序調用 image.show() 方法時,程序需要真正調用 BigImage 對象的 show() 方法,程序此時才真正創建 BigImage 對象。運行上面程序,看到如圖 6 所示的結果。
圖 6. 使用代理模式提高性能
看到如圖 6 所示的運行結果,讀者應該能認同:使用代理模式提高了獲取 Image 對象的系統性能。但可能有讀者會提出疑問:程序調用 ImageProxy 對象的 show() 方法時一樣需要創建 BigImage 對象啊,系統開銷并未真正減少啊?只是這種系統開銷延遲了而已啊?
我們可以從如下兩個角度來回答這個問題:
- 把創建 BigImage 推遲到真正需要它時才創建,這樣能保證前面程序運行的流暢性,而且能減少 BigImage 在內存中的存活時間,從宏觀上節省了系統的內存開銷。
- 有些情況下,也許程序永遠不會真正調用 ImageProxy 對象的 show() 方法——意味著系統根本無須創建 BigImage 對象。在這種情形下,使用代理模式可以顯著地提高系統運行性能。
與此完全類似的是,Hibernate 也是通過代理模式來“推遲”加載關聯實體的時間,如果程序并不需要訪問關聯實體,那程序就不會去抓取關聯實體了,這樣既可以節省系統的內存開銷,也可以縮短 Hibernate 加載實體的時間。
java 動態代理深度學習,
一.相關類及其方法:
java.lang.reflect.Proxy,
Proxy 提供用于創建動態代理類和實例的靜態方法.
newProxyInstance()
返回一個指定接口的代理類實例,該接口可以將方法調用指派到指定的調用處理程序
(詳見api文檔)
java.lang.reflect.InvocationHandler,
InvocationHandler 是代理實例的調用處理程序 實現的接口。?
invoke()
在代理實例上處理方法調用并返回結果。在與方法關聯的代理實例上調用方法時,將在調用處理程序上調用此方法。
(詳見api文檔)
二.源代碼:
被代理對象的接口及實現類:
package com.ml.test;
public interface Manager {
public void modify();
}
package com.ml.test;
public class ManagerImpl implements Manager {
@Override
public void modify() {
?? System.out.println("*******modify()方法被調用");
}
}
業務代理類:
package com.ml.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class BusinessHandler implements InvocationHandler {
private Object object = null;
public BusinessHandler(Object object) {
?? this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
??? throws Throwable {
?? System.out.println("do something before method");
?? Object ret = method.invoke(this.object, args);
?? System.out.println("do something after method");
?? return ret;
}
}
客戶端類:
package com.ml.test;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
?? // 元對象(被代理對象)
?? ManagerImpl managerImpl = new ManagerImpl();
?? // 業務代理類(在元對象的基礎上增加內容,比如這個函數運行需要時間,調試信息等)
?? BusinessHandler securityHandler = new BusinessHandler(managerImpl);
?? // 獲得代理類($Proxy0 extends Proxy implements Manager)的實例.
?? Manager managerProxy = (Manager) Proxy.newProxyInstance(managerImpl
???? .getClass().getClassLoader(), managerImpl.getClass()
???? .getInterfaces(), securityHandler);
?? managerProxy.modify();
}
}
managerImpl是被代理的類的對象,Manager是其接口類
securityHandler是代理managerImpl的對象,也就是說mnagerImpl是在securityHandler中以其成員變量的形式存在
Proxy.newProxyInstance(),傳入securityHandler對象,生成一個新類$Proxy0, securityHandler對象作為其的一個成員變量,這個新類實現了Manager接口,對于接口的每一個方法都是調用securityHandler的invoke函數,在invoke函數中調用的是managerImpl的對應的方法。生成$Proxy0的對象,就是Mangager managerPorxy.
如果是RPC模式的,怎么用代理呢?
由newProxyInstance()生成的代理類,其中成員變量就是繼承了InvocationHandler的類的對象,對于這個代理類,就可以看做是遠程服務器類(被代理類)的在本地的一個實例,完全可以把本地實例就看做是遠程服務器的實例,對于這個實例的每次函數的調用,就是把這次調用的函數名和參數傳給invoke函數,由invoke函數內部實現網絡通訊,就是把函數名和參數發給遠程服務器,服務器接收之后,把運行結果通過網絡返回
三.執行結果:
do something before method
*******modify()方法被調用
do something after method
四.機制分析:
Proxy.(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下幾件事.
(1)根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)創建代理類$Proxy.
$Proxy0類實現了interfaces的接口,并繼承了Proxy類.
(2)實例化$Proxy0并在構造方法中把BusinessHandler傳過去,接著$Proxy0調用父類Proxy的構造器,為h賦值,如下:
class Proxy{
?? InvocationHandler h=null;
?? protected Proxy(InvocationHandler h) {
??? this.h = h;
?? }
?? ...
}
下面是本例的$Proxy0類的源碼(好不容易才把它提出來):
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Manager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
?? try {
??? m1 = Class.forName("java.lang.Object").getMethod("equals",
????? new Class[] { Class.forName("java.lang.Object") });
??? m0 = Class.forName("java.lang.Object").getMethod("hashCode",
????? new Class[0]);
??? m3 = Class.forName("com.ml.test.Manager").getMethod("modify",
????? new Class[0]);
??? m2 = Class.forName("java.lang.Object").getMethod("toString",
????? new Class[0]);
?? } catch (NoSuchMethodException nosuchmethodexception) {
??? throw new NoSuchMethodError(nosuchmethodexception.getMessage());
?? } catch (ClassNotFoundException classnotfoundexception) {
??? throw new NoClassDefFoundError(classnotfoundexception.getMessage());
?? }
}
public $Proxy0(InvocationHandler invocationhandler) {
?? super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
?? try {
??? return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
????? .booleanValue();
?? } catch (Throwable throwable) {
??? throw new UndeclaredThrowableException(throwable);
?? }
}
@Override
public final int hashCode() {
?? try {
??? return ((Integer) super.h.invoke(this, m0, null)).intValue();
?? } catch (Throwable throwable) {
??? throw new UndeclaredThrowableException(throwable);
?? }
}
public final void modify() {
?? try {
??? super.h.invoke(this, m3, null);
??? return;
?? } catch (Error e) {
?? } catch (Throwable throwable) {
??? throw new UndeclaredThrowableException(throwable);
?? }
}
@Override
public final String toString() {
?? try {
??? return (String) super.h.invoke(this, m2, null);
?? } catch (Throwable throwable) {
??? throw new UndeclaredThrowableException(throwable);
?? }
}
}
接著把得到的$Proxy0實例強制轉換成Manager.
當執行managerProxy.modify()方法時,就調用了$Proxy0類中的modify()方法.
在modify方法中,調用父類Proxy中的h的invoke()方法.
即InvocationHandler.invoke();
/*** 相親接口* * @author zhengt* @time Jun 3, 2095 3:13:03 PM*/ public interface XiangQinInterface {/*** 相親方法*/public void xiangQin(); } /*** 張三相親實現類* * @author zhengt* @time Jun 3, 2095 3:14:48 PM*/ public class ZhangSanXiangQinInterfaceImpl implements XiangQinInterface {public void xiangQin() {System.out.println("張三去相親,娶個漂亮老婆。");} } import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;/*** 相親可是一輩子的大事,相親前要準備一下,打扮得帥氣些。* * @author zhengt* @time Jun 3, 2095 3:15:48 PM*/ public class ReadyInvocationHandler implements InvocationHandler {//相親接口的實現類,也就是張三相親類private Object zhangSan = null;public ReadyInvocationHandler(Object realSubject) {this.zhangSan = realSubject;}public Object invoke(Object proxy, Method m, Object[] args) {Object result = null;try {/*** 動態代理類$Proxy0調用xiangQin方法時會調用它自己的xiangQin方法,* 而它自己的xiangQin方法里面調用的是super.h.invoke(this, , ),也就是父類Proxy的h的invoke方法,* 也就是ReadyInvocationHandler類的invoke方法。* 所以,invoke(Object proxy, Method m, Object[] args)種的proxy實際上就是動態代理類$Proxy0,* 如果你將其強轉成XiangQinInterface然后調用它的xiangQin方法,然后它就會調用super.h.invoke(this, , ),這樣就會死循環。*//*** 網上關于這里最多問題就是Object proxy放在這里用來做什么呢?這個我也不知道,* 不過至少我們知道它到底是個什么東西,具體做什么用嘛就不得而知了*/System.out.println(proxy.getClass().getSimpleName());System.out.println("張三相親前,代理人給他打扮了打扮。");result = m.invoke(zhangSan, args);} catch (Exception ex) {System.exit(1);}return result;} } import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;/*** 張三來到了婚介所(相親現場),開始相親。* * @author zhengt* @time Jun 3, 2095 3:17:16 PM*/ public class HunJieSuo {public static void main(String args[]) {//先將張三相親這個相親的實現類實例化,也就是得到XiangQinInterface接口的一個實例對象XiangQinInterface zhangSan = new ZhangSanXiangQinInterfaceImpl();/*** 得到ZhangSanXiangQinInterfaceImpl這個類的一個代理類,同時為代理類綁定了一個處理類ReadyInvocationHandler。* 聽著很繞口,其實就是每次調用ZhangSanXiangQinInterfaceImpl這個子類的xiangQin方法時,* 不是zhangSan這個ZhangSanXiangQinInterfaceImpl類的實例去調用,* 而是這個ZhangSanXiangQinInterfaceImpl的代理類ReadyInvocationHandler去調用它自己的invoke方法,* 這個invoke方法里呢可以調用zhangSan這個實例的xiangQin方法*//*** 在java種怎樣實現動態代理呢* 第一步,我們要有一個接口,還要有一個接口的實現類,而這個實現類呢就是我們要代理的對象,* 所謂代理呢也就是在調用實現類的方法時,可以在方法執行前后做額外的工作,這個就是代理。* 第二步,我們要自己寫一個在要代理類的方法執行時,能夠做額外工作的類,而這個類必須繼承InvocationHandler接口,* 為什么要繼承它呢?因為代理類的實例在調用實現類的方法的時候,不會調真正的實現類的這個方法,* 而是轉而調用這個類的invoke方法(繼承時必須實現的方法),在這個方法中你可以調用真正的實現類的這個方法。* 第三步,在要用代理類的實例去調用實現類的方法的時候,寫出下面兩段代碼。*/XiangQinInterface proxy = (XiangQinInterface) Proxy.newProxyInstance(zhangSan.getClass().getClassLoader(),zhangSan.getClass().getInterfaces(),new ReadyInvocationHandler(zhangSan));proxy.xiangQin();/*** 這里要解釋下中部那段長長的代碼的意思,以及具體做了哪些工作?* 第一,根據zhangSan.getClass().getClassLoader()這個要代理類的類加載器和* zhangSan.getClass().getInterfaces()要代理類所實現的所有的接口* 作為參數調用Proxy.getProxyClass(ClassLoader loader, Class<?>... interfaces)* 的方法返回代理類的java.lang.Class對象,也就是得到了java動態生成的代理類$Proxy0的Class對象。* 同時,java還讓這個動態生成的$Proxy0類實現了要代理類的實現的所有接口,并繼承了Proxy接口。* 第二,實例化這個動態生成的$Proxy0類的一個實例,實例化代理類的構造函數為Proxy(InvocationHandler h),* 也就是說要實例化這個動態生成的$Proxy0類,必須給它一個InvocationHandler參數,也就是我們自己實現的用來在代理類* 方法執行前后做額外工作的類ReadyInvocationHandler。* 這段代碼Proxy.newProxyInstance(zhangSan.getClass().getClassLoader(),zhangSan.getClass().getInterfaces(),new ReadyInvocationHandler(zhangSan))* 得到的其實是一個類名叫$Proxy0 extends Proxy implements XiangQinInterface的類。* 第三,將這個$Proxy0類強制轉型成XiangQinInterface類型,調用xiangQin方法。*/} }
總結
以上是生活随笔為你收集整理的java 动态代理深度学习(Proxy,InvocationHandler)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java的容器的线程安全
- 下一篇: java 装饰器模式