Java動(dòng)態(tài)代理實(shí)現(xiàn)及原理分析

代理模式

給某個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)于原對(duì)象的訪問(wèn)

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

運(yùn)行時(shí)動(dòng)態(tài)生成代理類

動(dòng)態(tài)代理我們需要些什么

  1. 業(yè)務(wù)接口(Interface)
    業(yè)務(wù)的抽象表示
  2. 業(yè)務(wù)具體實(shí)現(xiàn)類(concreteManager)
    實(shí)現(xiàn)業(yè)務(wù)接口,執(zhí)行具體的業(yè)務(wù)操作
  3. 業(yè)務(wù)代理類($proxy,在運(yùn)行的時(shí)候動(dòng)態(tài)生成的類)
    進(jìn)行業(yè)務(wù)代理,調(diào)用業(yè)務(wù)代理操作類
  4. 業(yè)務(wù)代理操作類(proxyHandler,實(shí)現(xiàn)了InvocationHandler接口的類)
    代理方法的直接調(diào)用者,通過(guò)InvocationHandler中的invoke方法直接發(fā)起代理
  5. 客戶端調(diào)用對(duì)象(client)
    發(fā)起業(yè)務(wù)

接下來(lái)是具體實(shí)現(xiàn)

業(yè)務(wù)接口ICook

public interface ICook {

     void dealWithFood();

     void cook();
}

業(yè)務(wù)具體實(shí)現(xiàn)類CookManager

public class CookManager implements ICook {

    @Override
    public void dealWithFood() {
        System.out.println("food had been dealed with");
    }

    @Override
    public void cook() {
        System.out.println("cook food");
    }
}

業(yè)務(wù)代理操作類DynamicProxyHandler

public class DynamicProxyHandler implements InvocationHandler{
    Object realCookManager;
    DynamicProxyHandler(ICook realCookManager){
        this.realCookManager = realCookManager;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke start");
        System.out.println(method.getName());
        method.invoke(realCookManager,args);
        System.out.println("invoke end");
        return null;
    }
}

客戶端

public class Main {
    public static void main(String[] args){

        CookManager cookManager = new CookManager();
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(cookManager);
        ICook iCook =(ICook)Proxy.newProxyInstance(dynamicProxyHandler.getClass().getClassLoader(),cookManager.getClass().getInterfaces(), dynamicProxyHandler);
        //打印一下代理類的類名
        System.out.println(iCook.getClass().getName());
        iCook.dealWithFoot();
        iCook.cook();
    }
}

實(shí)現(xiàn)過(guò)程很簡(jiǎn)單,看一下輸出的結(jié)果吧

com.sun.proxy.$Proxy0
invoke start
dealWithFoot
food had been dealed with
invoke end
invoke start
cook
cook food
invoke end

輸出結(jié)果分析

輸出的業(yè)務(wù)代理類類名為$Proxy0,DynamicProxyHandler中的invoke方法被調(diào)用了,并且method.invoke方法會(huì)調(diào)用實(shí)現(xiàn)類中的具體實(shí)現(xiàn)方法,到這里我們其實(shí)就已經(jīng)完成了代理操作了,并且在DynamicProxyHandler的invoke中我們還可以添加自己的操作,比如打印個(gè)日志什么的,這里其實(shí)就是一次簡(jiǎn)單的應(yīng)用層級(jí)的hook的實(shí)現(xiàn)了。我們可以在客戶端發(fā)起調(diào)用的時(shí)候使用代理中的方法替換掉原有的具體實(shí)現(xiàn)方法并對(duì)其進(jìn)行擴(kuò)展,這樣我們?cè)诓桓淖冊(cè)袑?shí)現(xiàn)類的情況下增強(qiáng)了原有類的功能,符合開(kāi)閉原則。

知其然知其所以然,我們必須對(duì)自己有追求呀!動(dòng)態(tài)代理實(shí)現(xiàn)的技術(shù)點(diǎn)主要是反射,為了更好的理解其原理,看源碼是少不了的過(guò)程~~

來(lái),老板,上盤生肉!

我們從Proxy類中的newProxyInstance這個(gè)函數(shù)入手,為了更好的理解其原理,以下是精簡(jiǎn)后的代碼

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){
     //所有被實(shí)現(xiàn)的業(yè)務(wù)接口
      final Class<?>[] intfs = interfaces.clone();
     //尋找或生成指定的代理類
      Class<?> cl = getProxyClass0(loader, intfs);
      //通過(guò)反射類中的Constructor獲取其所有構(gòu)造方法
      final Constructor<?> cons = cl.getConstructor(constructorParams);
      //通過(guò)Constructor返回代理類的實(shí)例
      return cons.newInstance(new Object[]{h});
}

先看看這三個(gè)參數(shù)loader,interfaces,h;ClassLoader是一個(gè)抽象類,作用是將字節(jié)碼文件加載進(jìn)虛擬機(jī)并生成相應(yīng)的class(注意是小寫的),這里得到的loader是其子類AppClassLoader(負(fù)責(zé)加載應(yīng)用層字節(jié)碼)的一個(gè)實(shí)例,interfaces就是被實(shí)現(xiàn)的那些業(yè)務(wù)接口,h是InvocationHandler接口的實(shí)例,具體代理操作就被放在這個(gè)InvocationHandler的invoke函數(shù)中。

接下來(lái)看看生成業(yè)務(wù)代理類的getProxyClass0(loader,intfs)的實(shí)現(xiàn)

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
      // proxyClassCache會(huì)緩存所有的代理類,如果緩存中有這個(gè)業(yè)務(wù)代理類,則會(huì)從緩存中取出,否則從ProxyClassFactory中生成
        return proxyClassCache.get(loader, interfaces);
    }

ProxyClassFactory是Proxy中的內(nèi)部類,緩存中如果沒(méi)有這個(gè)代理類則會(huì)調(diào)用ProxyClassFactory中的apply方法生成

   private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
        // 這兩個(gè)常量就是代理類名字的由來(lái)
        private static final String proxyClassNamePrefix = "$Proxy";
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        
         @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
        //這里就生成了我們要的字節(jié)碼形式的代理類
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
        //defineClass0是個(gè)native方法        
        return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
        }
        
}

到這里業(yè)務(wù)代理類就生成了,我們?cè)倩氐絥ewProxyInstance方法中,它會(huì)將InvocationHandler的實(shí)例h傳入這個(gè)業(yè)務(wù)代理類實(shí)例中

   return cons.newInstance(new Object[]{h});

由于業(yè)務(wù)代理類是以字節(jié)碼形式存在于內(nèi)存中,我們想要看到其全貌可以將其保存下來(lái)然后反編譯查看其源碼

   byte[] proxyClassFile =  ProxyGenerator.generateProxyClass(iCook.getClass().getName(),cookManager.getClass().getInterfaces());
   saveToFile(proxyClassFile);

最終我們業(yè)務(wù)代理類$Proxy0類是這樣的!

public final class $Proxy0 extends Proxy implements ICook {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void cook() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void dealWithFoot() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.company.ICook").getMethod("cook", new Class[0]);
            m4 = Class.forName("com.company.ICook").getMethod("dealWithFoot", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

這個(gè)流程終于變得清晰了。
當(dāng)我們將業(yè)務(wù)接口ICook和業(yè)務(wù)代理操作類DynamicProxyHandler傳入Proxy中后,Proxy會(huì)為我們生成一個(gè)實(shí)現(xiàn)了ICook接口并繼承了Proxy的業(yè)務(wù)代理類$Proxy0。

在我們具體調(diào)用方法 iCook.dealWithFood()時(shí)它其實(shí)是調(diào)用了$Proxy0中的dealWithFood方法,然后再調(diào)用Proxy類的invoke方法,所以DynamicProxyHandler中的invoke方法才是最終執(zhí)行的方法,這個(gè)方法給了我們擴(kuò)展的可能并且最終我們實(shí)現(xiàn)了代理對(duì)象訪問(wèn)原對(duì)象的目的,也就是$Proxy0代理了CookManager。

我的第一篇博客+我拍的第一張感覺(jué)還不錯(cuò)的照片(ps功底略差。。。),希望以后可以堅(jiān)持下來(lái),分析可能有所不足,望不吝賜教!

DSC_0936.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容