自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解Java中的靜態(tài)代理和動(dòng)態(tài)代理

開發(fā) 后端
在代理模式(Proxy Pattern)中,一個(gè)類代表另一個(gè)類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。在代理模式中,我們創(chuàng)建具有現(xiàn)有對(duì)象的對(duì)象,以便向外界提供功能接口。

代理是一種設(shè)計(jì)模式

在代理模式(Proxy Pattern)中,一個(gè)類代表另一個(gè)類的功能。這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式。在代理模式中,我們創(chuàng)建具有現(xiàn)有對(duì)象的對(duì)象,以便向外界提供功能接口。目的:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。

類關(guān)系圖:

詳解Java中的靜態(tài)代理和動(dòng)態(tài)代理

靜態(tài)代理

創(chuàng)建一個(gè)接口,然后創(chuàng)建被代理的類實(shí)現(xiàn)該接口并且實(shí)現(xiàn)該接口中的抽象方法。之后再創(chuàng)建一個(gè)代理類,同時(shí)使其也實(shí)現(xiàn)這個(gè)接口。在代理類中持有一個(gè)被代理對(duì)象的引用,而后在代理類方法中調(diào)用該對(duì)象的方法。

代碼如下:

接口

  1. public interface HelloInterface{ 
  2.         void sayHello(); 

 被代理類

  1. public class Hello implements HelloInterface{ 
  2.        public void sayHello() { 
  3.                System.out.println("Hello Kevin!"); 
  4.        } 

 代理類

  1. public class HelloProxy implements HelloInterface{ 
  2.     private HelloInterface helloInterface=newHello(); 
  3.     public void sayHello() { 
  4.        System.out.println("Beforeinvoke sayHello" ); 
  5.        helloInterface.sayHello(); //調(diào)用被代理類Hello中的sayHello方法 
  6.        System.out.println("Afterinvoke sayHello"); 
  7.     } 

 代理類調(diào)用

被代理類被傳遞給了代理類HelloProxy,代理類在執(zhí)行具體方法時(shí)通過所持用的被代理類完成調(diào)用。

  1. public class ProxyTest { 
  2.        public static void main(String[] args) { 
  3.             HelloProxyhelloProxy=newHelloProxy(); 
  4.             helloProxy.sayHello(); 
  5.        } 

靜態(tài)代理的本質(zhì):由程序員創(chuàng)建或工具生成代理類的源碼,再編譯代理類。所謂靜態(tài)也就是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件,代理類和委托類的關(guān)系在運(yùn)行前就確定了。

動(dòng)態(tài)代理

動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成,所以不存在代理類的字節(jié)碼文件。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定。JDK中關(guān)于動(dòng)態(tài)代理的重要api如下:

java.lang.reflect.Proxy 這是Java 動(dòng)態(tài)代理機(jī)制生成的所有動(dòng)態(tài)代理類的父類,它提供了一組靜態(tài)方法來為一組接口動(dòng)態(tài)地生成代理類及其對(duì)象。 最重要的方法是:

  1. static Object newProxyInstance(ClassLoader loader, Class[]interfaces, InvocationHandler h)  

該方法用于為指定類裝載器、一組接口及調(diào)用處理器生成動(dòng)態(tài)代理類實(shí)例

java.lang.reflect.InvocationHandler 這是調(diào)用處理器接口,定義了一個(gè)invoke 方法,用于集中處理在動(dòng)態(tài)代理類對(duì)象上的方法調(diào)用,通常在該方法中實(shí)現(xiàn)對(duì)委托類的代理訪問。每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都要指定一個(gè)對(duì)應(yīng)的調(diào)用處理器對(duì)象。Object invoke(Object proxy, Method method, Object[] args) 該方法負(fù)責(zé)集中處理動(dòng)態(tài)代理類上的所有方法調(diào)用。第一個(gè)參數(shù)既是代理類實(shí)例,第二個(gè)參數(shù)是被調(diào)用的方法對(duì)象 ,第三個(gè)方法是調(diào)用參數(shù)。調(diào)用處理器根據(jù)這三個(gè)參數(shù)進(jìn)行預(yù)處理或分派到委托類實(shí)例上反射執(zhí)行

java.lang.ClassLoader 這是類裝載器類,負(fù)責(zé)將類的字節(jié)碼裝載到Java 虛擬機(jī)(JVM)中并為其定義類對(duì)象,然后該類才能被使用。Proxy靜態(tài)方法生成動(dòng)態(tài)代理類同樣需要通過類裝載器來進(jìn)行裝載才能使用,它與普通類的唯一區(qū)別就是其字節(jié)碼是由JVM 在運(yùn)行時(shí)動(dòng)態(tài)生成的而非預(yù)先存在于任何一個(gè).class 文件中。 每次生成動(dòng)態(tài)代理類對(duì)象時(shí)都需要指定一個(gè)類裝載器對(duì)象。我們來看一下動(dòng)態(tài)代理的實(shí)例:

接口

  1. public interface HelloInterface{ 
  2.         void sayHello(); 

 被代理類

  1. public class Hello implements HelloInterface{ 
  2.        public void sayHello() { 
  3.                System.out.println("Hello Kevin!"); 
  4.        } 

 實(shí)現(xiàn)InvocationHandler接口,創(chuàng)建自己的調(diào)用處理器

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.Method; 
  3. public class ProxyHandler implements InvocationHandler{ 
  4.     private Object object; 
  5.     public ProxyHandler(Object object){ 
  6.         this.object = object; 
  7.     } 
  8.        public Object invoke(Object proxy,Method method, Object[] args) throws Throwable { 
  9.              System.out.println("Before invoke "  + method.getName()); 
  10.              method.invoke(object, args); 
  11.              System.out.println("After invoke" + method.getName()); 
  12.              return null
  13.        } 

 測(cè)試類

  1. import java.lang.reflect.InvocationHandler; 
  2. import java.lang.reflect.Proxy; 
  3. public class DynamicProxyTest{ 
  4.         public static void main(String[] args) { 
  5.            HelloInterface hello = new Hello(); 
  6.           //把hello實(shí)例傳入動(dòng)態(tài)代理處理器 
  7.            InvocationHandler handler = new ProxyHandler(hello); 
  8.            //生成動(dòng)態(tài)代理類實(shí)例 
  9.       HelloInterface proxyHello = (HelloInterface)Proxy.newProxyInstance(hello.getClass().getClassLoader(),hello.getClass().getInterfaces(), handler); 
  10.                proxyHello.sayHello(); 
  11.            } 

 運(yùn)行代碼

  1. Before invoke sayHello 
  2.  
  3. Hello Kevin! 
  4.  
  5. After invoke sayHello 

 我們可以看到,動(dòng)態(tài)代理與靜態(tài)代理相比較,最大的好處是接口中聲明的所有方法都被轉(zhuǎn)移到調(diào)用處理器一個(gè)集中的方法中處理(InvocationHandler.invoke),生成不同類的代理實(shí)例我們只需要在類DynamicProxyTest中處理即可;而靜態(tài)代理需要代理多個(gè)類的時(shí)候,由于代理對(duì)象要實(shí)現(xiàn)與目標(biāo)對(duì)象一致的接口,則會(huì)遇到下面的問題:

只維護(hù)一個(gè)代理類,由這個(gè)代理類實(shí)現(xiàn)多個(gè)接口,但是這樣就導(dǎo)致代理類過于龐大;

新建多個(gè)代理類,每個(gè)目標(biāo)對(duì)象對(duì)應(yīng)一個(gè)代理類,但是這樣會(huì)產(chǎn)生過多的代理類當(dāng)接口;

需要增加、刪除、修改方法的時(shí)候,目標(biāo)對(duì)象與代理類都要同時(shí)修改,不易維護(hù)。

動(dòng)態(tài)代理類也有小小的遺憾,那就是它只能為接口創(chuàng)建代理!如果想對(duì)沒有實(shí)現(xiàn)接口的類創(chuàng)建代理則無能為力。為了解決這種情況,我們通常使用cglib技術(shù),其在AOP(例如spring)和ORM(例如Hibernate)中有廣泛的應(yīng)用,在這里就不對(duì)cglib進(jìn)行展開介紹了。

動(dòng)態(tài)代理類的生成

我們?cè)賮砜匆粋€(gè)實(shí)例,修改類DynamicProxyTest,代碼如下:

  1. public static void main(String[] args) { 
  2.                System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");  
  3.                HelloInterface hello = new Hello(); 
  4.                InvocationHandler handler = new ProxyHandler(hello); 
  5.                HelloInterface proxyHello=(HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(),handler); 
  6.                proxyHello.sayHello(); 
  7.                System.out.println(proxyHello.getClass().getName()); 
  8.            } 

 運(yùn)行結(jié)果

  1. Before invoke sayHello 
  2.  
  3. Hello Kevin! 
  4.  
  5. After invoke sayHello 
  6.  
  7. com.sun.proxy.$Proxy0 

 我們發(fā)現(xiàn)proxyHello的類型是.$Proxy0而不是HelloInterface。我們通過反編譯來查看$Proxy0的源碼,在工程的com.sun.proxy目錄下。注意:必須添加下面的代碼

  1. System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); 

$Proxy0就是由JDK創(chuàng)建的動(dòng)態(tài)代理類,是在運(yùn)行時(shí)創(chuàng)建生成的。動(dòng)態(tài)代理類的格式是“$ProxyN”,其中 N 是一個(gè)逐一遞增的阿拉伯?dāng)?shù)字,代表Proxy 類第N 次生成的動(dòng)態(tài)代理類,并不是每次調(diào)用Proxy 的靜態(tài)方法創(chuàng)建動(dòng)態(tài)代理類都會(huì)使得N 值增加,原因是如果對(duì)同一組接口(包括接口排列的順序相同)試圖重復(fù)創(chuàng)建動(dòng)態(tài)代理類,它會(huì)很聰明地返回先前已經(jīng)創(chuàng)建好的代理類的類對(duì)象,而不會(huì)再嘗試去創(chuàng)建一個(gè)全新的代理類,這樣可以節(jié)省不必要的代碼重復(fù)生成,提高了代理類的創(chuàng)建效率。$Proxy0源碼如下:

  1. import com.my.demo2.HelloInterface; 
  2. import java.lang.reflect.InvocationHandler; 
  3. import java.lang.reflect.Method; 
  4. import java.lang.reflect.Proxy; 
  5. import java.lang.reflect.UndeclaredThrowableException; 
  6. public final class $Proxy0 extends Proxy implements HelloInterface { 
  7.   private static Method m1; 
  8.   private static Method m3; 
  9.   private static Method m2; 
  10.   private static Method m0; 
  11.   public $Proxy0(InvocationHandlerparamInvocationHandler) { super(paramInvocationHandler); } 
  12.   public final boolean equals(ObjectparamObject) { 
  13.     try { 
  14.       return ((Boolean)this.h.invoke(this,m1, new Object[] { paramObject })).booleanValue(); 
  15.     } catch (Error|RuntimeExceptionerror) { 
  16.       throw null
  17.     } catch (Throwable throwable) { 
  18.       throw newUndeclaredThrowableException(throwable); 
  19.     } 
  20.   } 
  21.   public final void sayHello() { 
  22.     try { 
  23.       this.h.invoke(this, m3, null); 
  24.       return
  25.     } catch (Error|RuntimeExceptionerror) { 
  26.       throw null
  27.     } catch (Throwable throwable) { 
  28.       throw newUndeclaredThrowableException(throwable); 
  29.     } 
  30.   } 
  31.   public final String toString() { 
  32.     try { 
  33.       return (String)this.h.invoke(this,m2, null); 
  34.     } catch (Error|RuntimeExceptionerror) { 
  35.       throw null
  36.     } catch (Throwable throwable) { 
  37.       throw newUndeclaredThrowableException(throwable); 
  38.     } 
  39.   } 
  40.   public final int hashCode() { 
  41.     try { 
  42.       return ((Integer)this.h.invoke(this,m0, null)).intValue(); 
  43.     } catch (Error|RuntimeExceptionerror) { 
  44.       throw null
  45.     } catch (Throwable throwable) { 
  46.       throw newUndeclaredThrowableException(throwable); 
  47.     } 
  48.   } 
  49.   static { 
  50.     try { 
  51.       m1 = Class.forName("java.lang.Object").getMethod("equals",new Class[] { Class.forName("java.lang.Object") }); 
  52.       m3 = Class.forName("com.my.demo2.HelloInterface").getMethod("sayHello",new Class[0]); 
  53.       m2 = Class.forName("java.lang.Object").getMethod("toString",new Class[0]); 
  54.       m0 = Class.forName("java.lang.Object").getMethod("hashCode",new Class[0]); 
  55.       return
  56.     } catch (NoSuchMethodExceptionnoSuchMethodException) { 
  57.       throw newNoSuchMethodError(noSuchMethodException.getMessage()); 
  58.     } catch (ClassNotFoundExceptionclassNotFoundException) { 
  59.       throw new NoClassDefFoundError(classNotFoundException.getMessage()); 
  60.     } 
  61.   } 

 從上面的代碼中我們可以看到:

1. 在代理類$ProxyN的實(shí)例上調(diào)用其代理的接口中所聲明的方法時(shí),這些方法最終都會(huì)由調(diào)用處理器的invoke 方法執(zhí)行;

2. 代理類的根類java.lang.Object 中的三個(gè)方法:hashCode,equals 和 toString也同樣會(huì)被分派到調(diào)用處理器的invoke 方法中執(zhí)行。

靜態(tài)代理和動(dòng)態(tài)代理最重要的四個(gè)知識(shí)點(diǎn)

1.靜態(tài)代理在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件中確認(rèn)了代理類和委托類的關(guān)系;

2.動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成,所以不存在代理類的字節(jié)碼文件。代理類和委托類的關(guān)系是在程序運(yùn)行時(shí)確定。 動(dòng)態(tài)代理根據(jù)接口或目標(biāo)對(duì)象,計(jì)算出代理類的字節(jié)碼,然后再加載到JVM中使用。其實(shí)現(xiàn)原理如下:由于JVM通過字節(jié)碼的二進(jìn)制信息加載類的,那么,如果我們?cè)谶\(yùn)行期系統(tǒng)中,遵循Java編譯系統(tǒng)組織.class文件的格式和結(jié)構(gòu),生成相應(yīng)的二進(jìn)制數(shù)據(jù),然后再把這個(gè)二進(jìn)制數(shù)據(jù)加載轉(zhuǎn)換成對(duì)應(yīng)的類,這樣,就完成了在代碼中,動(dòng)態(tài)創(chuàng)建一個(gè)類的能力了。

詳解Java中的靜態(tài)代理和動(dòng)態(tài)代理

3.靜態(tài)代理的缺點(diǎn)是在程序規(guī)模稍大時(shí),維護(hù)代理類的成本高,靜態(tài)代理無法勝任;

4.動(dòng)態(tài)代理只能為實(shí)現(xiàn)了接口的類創(chuàng)建代理。

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2023-12-06 08:23:44

代理模式設(shè)計(jì)模式

2011-04-06 11:41:25

Java動(dòng)態(tài)代理

2023-02-24 07:42:30

Java動(dòng)態(tài)代理

2021-01-14 05:16:09

MyBatis動(dòng)態(tài)代理

2010-06-30 10:39:45

Linux SNMP

2015-09-22 11:09:47

Java 8動(dòng)態(tài)代理

2011-03-23 10:40:51

java代理模式

2012-08-28 10:59:26

JavaJava動(dòng)態(tài)代理Proxy

2009-12-28 15:45:22

動(dòng)態(tài)網(wǎng)絡(luò)接入控制

2015-09-28 15:59:00

Java動(dòng)態(tài)代理機(jī)制

2017-05-11 21:30:01

Android動(dòng)態(tài)代理ServiceHook

2017-10-12 14:56:11

2022-09-01 10:40:29

SpringAOPJDK

2023-09-08 00:07:41

2011-11-17 14:32:45

Java靜態(tài)代理動(dòng)態(tài)代理

2010-05-25 17:35:12

SVN代理

2024-01-04 07:42:44

JavaCGLIBJDK

2024-07-22 15:34:25

2018-11-12 12:17:00

2015-09-24 08:55:14

Java動(dòng)態(tài)代理擴(kuò)展
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)