教育行業(yè)A股IPO第一股(股票代碼 003032)

全國(guó)咨詢/投訴熱線:400-618-4000

一文詳解Proxy動(dòng)態(tài)代理的內(nèi)部機(jī)制

更新時(shí)間:2017年11月13日14時(shí)25分 來(lái)源:傳智播客 瀏覽次數(shù):

機(jī)制分析:其實(shí)JDK的動(dòng)態(tài)代理,實(shí)際上就是“反射”與“執(zhí)行時(shí)動(dòng)態(tài)生成字節(jié)碼”二者的結(jié)合體

$Proxy0=Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)做了以下2件事。

(1)根據(jù)參數(shù)loader和interfaces調(diào)用方法 Proxy的getProxyClass(loader, interfaces)創(chuàng)建代理類$Proxy0。

$Proxy0類實(shí)現(xiàn)了interfaces的接口,并繼承了Proxy類。

(2)實(shí)例化$Proxy0并在構(gòu)造方法中把InvocationHandler傳過(guò)去,接著$Proxy0調(diào)用父類Proxy的構(gòu)造器,為h賦值

// Proxy類中有一個(gè)protected構(gòu)造方法,接收一個(gè)InvocationHandler
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;
 // IManager是代理實(shí)現(xiàn)的的接口,下面主要介紹了如何代理
 public final class $Proxy0 extends Proxy implements IManager {
 // 調(diào)用父類Proxy的構(gòu)造方法傳遞InvocationHandler 
 public $Proxy0(InvocationHandler invocationhandler) {
    super(invocationhandler);
 }
 private static Method m1;
 private static Method m0;
 private static Method m3;
 private static Method m2;
 ....
 static {
    try {
     // 反射得到Object的方法對(duì)象(Method對(duì)象)
     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());
    }
 }
 .......
 // 當(dāng)調(diào)用代理類$proxy0的方法時(shí),會(huì)回調(diào)$Proxy0=Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,    //  InvocationHandler h)中定義的InvocationHandler的Invoke()方法
 //          三個(gè)參數(shù):this---代理類本身$Proxy0
 //          m1---相對(duì)應(yīng)的Method對(duì)象
 //          new Object[]{}---方法的參數(shù),沒(méi)有參數(shù)傳null
 @Override
 public final boolean equals(Object obj) {
    try {
    // 這句代碼回調(diào)了你寫的invoke()方法
     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的使用
 //  把得到的$Proxy0實(shí)例強(qiáng)制轉(zhuǎn)換成IManager類型的managerProxy.
 IManager managerProxy = (Manager) Proxy.newProxyInstance(managerImpl
      .getClass().getClassLoader(), managerImpl.getClass()
      .getInterfaces(), invocationHandler);
 $Proxy0也實(shí)現(xiàn)了IManager接口中的所有方法,所以當(dāng)調(diào)用managerProxy.modify(),就是執(zhí)行下面這些代碼
   try {
     super.h.invoke(this, m3, null);
     return;
    } catch (Error e) {
    } catch (Throwable throwable) {
     throw new UndeclaredThrowableException(throwable);
 }  

這樣動(dòng)態(tài)代理機(jī)制就實(shí)現(xiàn)了。

所以JAVA的動(dòng)態(tài)代理的關(guān)鍵就在Proxy.newProxyInstance(..)方法執(zhí)行時(shí)生成了$Proxy0的內(nèi)存字節(jié)碼以及JDK的反射機(jī)制!?

本文版權(quán)歸傳智播客java學(xué)院所有,歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明作者出處。謝謝!
0 分享到:
和我們?cè)诰€交談!