1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        從源碼的角度搞懂 Java 動(dòng)態(tài)代理!

        共 13517字,需瀏覽 28分鐘

         ·

        2020-12-16 23:10

        Java技術(shù)棧

        www.javastack.cn

        關(guān)注閱讀更多優(yōu)質(zhì)文章



        前言

        最近,看了一下關(guān)于RMI(Remote Method Invocation)相關(guān)的知識,遇到了一個(gè)動(dòng)態(tài)代理的問題,然后就決定探究一下動(dòng)態(tài)代理。

        這里先科普一下RMI。

        RMI

        像我們平時(shí)寫的程序,對象之間互相調(diào)用方法都是在同一個(gè)JVM中進(jìn)行,而RMI可以實(shí)現(xiàn)一個(gè)JVM上的對象調(diào)用另一個(gè)JVM上對象的方法,即遠(yuǎn)程調(diào)用。

        接口定義

        定義一個(gè)遠(yuǎn)程對象接口,實(shí)現(xiàn)Remote接口來進(jìn)行標(biāo)記。

        public?interface?UserInterface?extends?Remote?{
        ????void?sayHello()?throws?RemoteException;
        }

        遠(yuǎn)程對象定義

        定義一個(gè)遠(yuǎn)程對象類,繼承UnicastRemoteObject來實(shí)現(xiàn)Serializable和Remote接口,并實(shí)現(xiàn)接口方法。

        public?class?User?extends?UnicastRemoteObject?implements?UserInterface?{
        ????public?User()?throws?RemoteException?{}
        ????@Override
        ????public?void?sayHello()?{
        ????????System.out.println("Hello?World");
        ????}
        }

        服務(wù)端

        啟動(dòng)服務(wù)端,將user對象在注冊表上進(jìn)行注冊。

        public?class?RmiServer?{
        ????public?static?void?main(String[]?args)?throws?RemoteException,?AlreadyBoundException,?MalformedURLException?{
        ????????User?user?=?new?User();
        ????????LocateRegistry.createRegistry(8888);
        ????????Naming.bind("rmi://127.0.0.1:8888/user",?user);
        ????????System.out.println("rmi?server?is?starting...");
        ????}
        }

        啟動(dòng)服務(wù)端:

        客戶端

        從服務(wù)端注冊表獲取遠(yuǎn)程對象,在服務(wù)端調(diào)用sayHello()方法。

        public?class?RmiClient?{
        ????public?static?void?main(String[]?args)?throws?RemoteException,?NotBoundException,?MalformedURLException?{
        ????????UserInterface?user?=?(UserInterface)?Naming.lookup("rmi://127.0.0.1:8888/user");
        ????????user.sayHello();
        ????}
        }

        服務(wù)端運(yùn)行結(jié)果:至此,一個(gè)簡單的RMI demo完成。

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

        提出問題

        看了看RMI代碼,覺得UserInterface這個(gè)接口有點(diǎn)多余,如果客戶端使用Naming.lookup()獲取的對象不強(qiáng)轉(zhuǎn)成UserInterface,直接強(qiáng)轉(zhuǎn)成User是不是也可以,于是試了一下,就報(bào)了以下錯(cuò)誤:似曾相識又有點(diǎn)陌生的$Proxy0,翻了翻塵封的筆記找到了是動(dòng)態(tài)代理的知識點(diǎn),寥寥幾筆帶過,所以決定梳理一下動(dòng)態(tài)代理,重新整理一份筆記。

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

        接口定義

        public?interface?UserInterface?{
        ????void?sayHello();
        }

        真實(shí)角色定義

        public?class?User?implements?UserInterface?{
        ????@Override
        ????public?void?sayHello()?{
        ????????System.out.println("Hello?World");
        ????}
        }

        調(diào)用處理類定義

        代理類調(diào)用真實(shí)角色的方法時(shí),其實(shí)是調(diào)用與真實(shí)角色綁定的處理類對象的invoke()方法,而invoke()調(diào)用的是真實(shí)角色的方法。

        這里需要實(shí)現(xiàn) InvocationHandler 接口以及invoke()方法。

        public?class?UserHandler?implements?InvocationHandler?{
        ????private?User?user;
        ????public?UserProxy(User?user)?{
        ????????this.user?=?user;
        ????}
        ????@Override
        ????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
        ????????System.out.println("invoking?start....");
        ????????method.invoke(user);
        ????????System.out.println("invoking?stop....");
        ????????return?user;
        ????}
        }

        執(zhí)行類

        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ????????User?user?=?new?User();
        ????????//?處理類和真實(shí)角色綁定
        ????????UserHandler?userHandler?=?new?UserHandler(user);
        ????????//?開啟將代理類class文件保存到本地模式,平時(shí)可以省略
        ????????System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",?"true");
        ????????//?動(dòng)態(tài)代理生成代理對象$Proxy0
        ????????Object?o?=?Proxy.newProxyInstance(Main.class.getClassLoader(),?new?Class[]{UserInterface.class},?userHandler);
        ????????//?調(diào)用的其實(shí)是invoke()
        ????????((UserInterface)o).sayHello();
        ????}

        運(yùn)行結(jié)果:這樣動(dòng)態(tài)代理的基本用法就學(xué)完了,可是還有好多問題不明白。

        1. 動(dòng)態(tài)代理是怎么調(diào)用的invoke()方法?
        2. 處理類UserHandler有什么作用?
        3. 為什么要將類加載器和接口類數(shù)組當(dāng)作參數(shù)傳入newProxyInstance?

        假如讓你去實(shí)現(xiàn)動(dòng)態(tài)代理,你有什么設(shè)計(jì)思路?

        猜想

        動(dòng)態(tài)代理,是不是和靜態(tài)代理,即設(shè)計(jì)模式的代理模式有相同之處呢?

        簡單捋一捋代理模式實(shí)現(xiàn)原理:真實(shí)角色和代理角色共同實(shí)現(xiàn)一個(gè)接口并實(shí)現(xiàn)抽象方法A,代理類持有真實(shí)角色對象,代理類在A方法中調(diào)用真實(shí)角色對象的A方法。在Main中實(shí)例化代理對象,調(diào)用其A方法,間接調(diào)用了真實(shí)角色的A方法。

        「實(shí)現(xiàn)代碼」

        //?接口和真實(shí)角色對象就用上面代碼
        //?代理類,實(shí)現(xiàn)UserInterface接口
        public?class?UserProxy?implements?UserInterface?{
        ???//?持有真實(shí)角色對象
        ????private?User?user?=?new?User();
        ????@Override
        ????public?void?sayHello()?{
        ????????System.out.println("invoking?start....");
        ????????//?在代理對象的sayHello()里調(diào)用真實(shí)角色的sayHello()
        ????????user.sayHello();
        ????????System.out.println("invoking?stop....");
        ????}
        }
        //?運(yùn)行類
        public?class?Main?{
        ????public?static?void?main(String[]?args)?{
        ???????//?實(shí)例化代理角色對象
        ????????UserInterface?userProxy?=?new?UserProxy();
        ????????//?調(diào)用了代理對象的sayHello(),其實(shí)是調(diào)用了真實(shí)角色的sayHello()
        ????????userProxy.sayHello();
        ????}

        拿開始的動(dòng)態(tài)代理代碼和靜態(tài)代理比較,接口、真實(shí)角色都有了,區(qū)別就是多了一個(gè)UserHandler處理類,少了一個(gè)UserProxy代理類。

        接著對比一下兩者的處理類和代理類,發(fā)現(xiàn)UserHandler的invoke()和UserProxy的sayHello()這兩個(gè)方法的代碼都是一樣的。那么,是不是新建一個(gè)UserProxy類,然后實(shí)現(xiàn)UserInterface接口并持有UserHandler的對象,在sayHello()方法中調(diào)用UserHandler的invoke()方法,就可以動(dòng)態(tài)代理了。

        「代碼大概就是這樣的」

        //?猜想的代理類結(jié)構(gòu),動(dòng)態(tài)代理生成的代理是com.sun.proxy.$Proxy0
        public?class?UserProxy?implements?UserInterface{
        ???//?持有處理類的對象
        ????private?InvocationHandler?handler;
        ????public?UserProxy(InvocationHandler?handler)?{
        ????????this.handler?=?handler;
        ????}
        ????//?實(shí)現(xiàn)sayHello()方法,并調(diào)用invoke()
        ????@Override
        ????public?void?sayHello()?{
        ????????try?{
        ????????????handler.invoke(this,?UserInterface.class.getMethod("sayHello"),?null);
        ????????}?catch?(Throwable?throwable)?{
        ????????????throwable.printStackTrace();
        ????????}
        ????}
        }
        //?執(zhí)行類
        public?static?void?main(String[]?args)?{
        ????????User?user?=?new?User();
        ????????UserHandler?userHandler?=?new?UserHandler(user);
        ????????UserProxy?proxy?=?new?UserProxy(userHandler);
        ????????proxy.sayHello();
        ????}

        輸出結(jié)果:

        上面的代理類代碼是寫死的,而動(dòng)態(tài)代理是當(dāng)你調(diào)用Proxy.newProxyInstance()時(shí),會(huì)根據(jù)你傳入的參數(shù)來動(dòng)態(tài)生成這個(gè)代理類代碼,如果讓我實(shí)現(xiàn),會(huì)是以下這個(gè)流程。

        1. 根據(jù)你傳入的Class[]接口數(shù)組,代理類會(huì)來實(shí)現(xiàn)這些接口及其方法(這里就是sayHello()),并且持有你傳入的userHandler對象,使用文件流將預(yù)先設(shè)定的包名、類名、方法名等一行行代碼寫到本地磁盤,生成$Proxy0.java文件
        2. 使用編譯器將Proxy0.class
        3. 根據(jù)你傳入的ClassLoader將$Proxy0.class加載到JMV中
        4. 調(diào)用Proxy.newProxyInstance()就會(huì)返回一個(gè)$Proxy0的對象,然后調(diào)用sayHello(),就執(zhí)行了里面userHandler的invoke()

        以上就是對動(dòng)態(tài)代理的一個(gè)猜想過程,下面就通過debug看看源碼是怎么實(shí)現(xiàn)的。


        在困惑的日子里學(xué)會(huì)擁抱源碼

        擁抱源碼

        調(diào)用流程圖

        這里先用PPT畫一個(gè)流程圖,可以跟著流程圖來看后面的源碼。

        流程圖

        「從newProxyInstance()設(shè)置斷點(diǎn)」

        newProxyInstance()

        newProxyInstance()代碼分為上下兩部分,上部分是獲取類Proxy0對象。

        「上部分代碼」

        newProxyInstance()

        從名字看就知道getProxyClass0()是核心方法,step into

        getProxyClass0()

        getProxyClass()

        里面調(diào)用了WeakCache對象的get()方法,這里暫停一下debug,先講講WeakCache類。

        WeakCache

        顧名思義,它是一個(gè)弱引用緩存。那什么是是弱引用呢,是不是還有強(qiáng)引用呢?

        弱引用

        WeakReference就是弱引用類,作為包裝類來包裝其他對象,在進(jìn)行GC時(shí),其中的包裝對象會(huì)被回收,而WeakReference對象會(huì)被放到引用隊(duì)列中。

        舉個(gè)栗子:

        ?//?這就是強(qiáng)引用,只要不寫str1?=?null,str1指向的這個(gè)字符串不就會(huì)被垃圾回收
        ?String?str1?=?new?String("hello");
        ?ReferenceQueue?referenceQueue?=?new?ReferenceQueue();
        ?//?只要垃圾回收,這個(gè)str2里面包裝的對象就會(huì)被回收,但是這個(gè)弱引用對象不會(huì)被回收,即word會(huì)被回收,但是str2指向的弱引用對象不會(huì)
        ?//?每個(gè)弱引用關(guān)聯(lián)一個(gè)ReferenceQueue,當(dāng)包裝的對象被回收,這個(gè)弱引用對象會(huì)被放入引用隊(duì)列中
        ?WeakReference?str2?=?new?WeakReference<>(new?String("world"),?referenceQueue);
        ?//?執(zhí)行g(shù)c
        ?System.gc();
        ?Thread.sleep(3);
        ?//?輸出被回收包裝對象的弱引用對象:java.lang.ref.WeakReference@2077d4de
        ?//?可以debug看一下,弱引用對象的referent變量指向的包裝對象已經(jīng)為null
        ?System.out.println(referenceQueue.poll());

        WeakCache的結(jié)構(gòu)

        其實(shí)整個(gè)WeakCache的都是圍繞著成員變量map來工作的,構(gòu)建了一個(gè)一個(gè)>格式的二級緩存,在動(dòng)態(tài)代理中對應(yīng)的類型是<類加載器, <接口Class, 代理Class>>,它們都使用了弱引用進(jìn)行包裝,這樣在垃圾回收的時(shí)候就可以直接回收,減少了堆內(nèi)存占用。

        //?存放已回收弱引用的隊(duì)列
        private?final?ReferenceQueue?refQueue?=?new?ReferenceQueue<>();
        //?使用ConcurrentMap實(shí)現(xiàn)的二級緩存結(jié)構(gòu)
        private?final?ConcurrentMap>>?map?=?new?ConcurrentHashMap<>();
        //?可以不關(guān)注這個(gè),這個(gè)是用來標(biāo)識二級緩存中的value是否存在的,即Supplier是否被回收
        private?final?ConcurrentMap,?Boolean>?reverseMap?=?new?ConcurrentHashMap<>();
        //?包裝傳入的接口class,生成二級緩存的Key
        private?final?BiFunction?subKeyFactory?=?new?KeyFactory();
        //?包裝$Proxy0,生成二級緩存的Value
        private?final?BiFunction?valueFactory?=?new?ProxyClassFactory();

        WeakCache的get()

        回到debug,接著進(jìn)入get()方法,看看map二級緩存是怎么生成KV的。

        ?public?V?get(K?key,?P?parameter)?{
        ????????Objects.requireNonNull(parameter);
        ????????//?遍歷refQueue,然后將緩存map中對應(yīng)的失效value刪除
        ????????expungeStaleEntries();
        ????????//?以ClassLoader為key,構(gòu)建map的一級緩存的Key,是CacheKey對象
        ????????Object?cacheKey?=?CacheK.valueOf(key,?refQueue);
        ????????//?通過Key從map中獲取一級緩存的value,即ConcurrentMap
        ????????ConcurrentMap>?valuesMap?=?map.get(cacheKey);
        ????????if?(valuesMap?==?null)?{
        ?????????//?如果Key不存在,就新建一個(gè)ConCurrentMap放入map,這里使用的是putIfAbsent
        ?????????//?如果key已經(jīng)存在了,就不覆蓋并返回里面的value,不存在就返回null并放入Key
        ?????????//?現(xiàn)在緩存map的結(jié)構(gòu)就是ConCurrentMap>
        ????????????ConcurrentMap>?oldValuesMap?=?map.putIfAbsent(cacheKey,?valuesMap?=?new?ConcurrentHashMap<>());
        ????????????//?如果其他線程已經(jīng)創(chuàng)建了這個(gè)Key并放入就可以復(fù)用了
        ????????????if?(oldValuesMap?!=?null)?{
        ????????????????valuesMap?=?oldValuesMap;
        ????????????}
        ????????}
        ????????//?生成二級緩存的subKey,現(xiàn)在緩存map的結(jié)構(gòu)就是ConCurrentMap>
        ????????//?看后面的<生成二級緩存Key>?。?!
        ????????Object?subKey?=?Objects.requireNonNull(subKeyFactory.apply(key,?parameter));
        ????????//?根據(jù)二級緩存的subKey獲取value
        ????????Supplier?supplier?=?valuesMap.get(subKey);
        ????????Factory?factory?=?null;
        ????????
        //?!?。≈钡酵瓿啥壘彺鎂alue的構(gòu)建才結(jié)束,Value是弱引用的$Proxy0.class?。。?/span>
        ????????while?(true)?{
        ???????????//?第一次循環(huán):suppiler肯定是null,因?yàn)檫€沒有將放入二級緩存的KV值
        ???????????//?第二次循環(huán):這里suppiler不為null了?。?!進(jìn)入if
        ????????????if?(supplier?!=?null)?{
        ????????????????//?第二次循環(huán):真正生成代理對象,
        ????????????????//?往后翻,看<生成二級緩存Value>,核心!!!!!
        ????????????????//?看完后面回到這里:value就是弱引用后的$Proxy0.class
        ????????????????V?value?=?supplier.get();
        ????????????????if?(value?!=?null)?{
        ?????????????//?本方法及上部分的最后一行代碼,跳轉(zhuǎn)最后的<構(gòu)建$Proxy對象>
        ????????????????????return?value;
        ????????????????}
        ????????????}
        ??????????//?第一次循環(huán):factory肯定為null,生成二級緩存的Value
        ????????????if?(factory?==?null)?{
        ????????????????factory?=?new?Factory(key,?parameter,?subKey,?valuesMap);
        ????????????}
        ?????????//?第一次循環(huán):將subKey和factory作為KV放入二級緩存
        ????????????if?(supplier?==?null)?{
        ????????????????supplier?=?valuesMap.putIfAbsent(subKey,?factory);
        ????????????????if?(supplier?==?null)?{
        ????????????????????//?第一次循環(huán):賦值之后suppiler就不為空了,記?。。。。?!
        ????????????????????supplier?=?factory;
        ????????????????}
        ????????????}?
        ???????????}
        ????????}
        ????}

        生成二級緩存Key

        在get()中調(diào)用subKeyFactory.apply(key, parameter),根據(jù)你newProxyInstance()傳入的接口Class[]的個(gè)數(shù)來生成二級緩存的Key,這里我們就傳入了一個(gè)UserInterface.class,所以就返回了Key1對象。

        KeyFactory.apply()

        不論是Key1、Key2還是KeyX,他們都繼承了WeakReference,都是包裝對象是Class的弱引用類。這里看看Key1的代碼。

        Key1

        生成二級緩存Value

        在上面的while循環(huán)中,第一次循環(huán)只是生成了一個(gè)空的Factory對象放入了二級緩存的ConcurrentMap中。

        在第二次循環(huán)中,才開始通過get()方法來真正的構(gòu)建value。

        別回頭,接著往下看。

        Factory.get()生成弱引用value

        「CacheValue」類是一個(gè)弱引用,是二級緩存的Value值,包裝的是class,在這里就是$Proxy0.class,至于這個(gè)類如何生成的,根據(jù)下面代碼注釋一直看完Class文件的生成

        public?synchronized?V?get()?{
        ????????????//?檢查是否被回收,如果被回收,會(huì)繼續(xù)執(zhí)行上面的while循環(huán),重新生成Factory
        ????????????Supplier?supplier?=?valuesMap.get(subKey);
        ????????????if?(supplier?!=?this)?{
        ????????????????return?null;
        ????????????}
        ????????????//?這里的V的類型是Class
        ????????????V?value?=?null;
        ????????????//?這行是核心代碼,看后面,記住這里返回的是Class
        ????????????value?=?Objects.requireNonNull(valueFactory.apply(key,?parameter));
        ????????????//?將Class對象包裝成弱引用
        ????????????CacheValue?cacheValue?=?new?CacheValue<>(value);
        ????????????//?回到上面V?value?=?supplier.get();
        ????????????return?value;
        ????????}
        ????}
        CacheValue




        Class文件的生成

        包名類名的定義與驗(yàn)證

        進(jìn)入valueFactory.apply(key, parameter)方法,看看class文件是怎么生成的。

        ?private?static?final?String?proxyClassNamePrefix?=?"$Proxy";

        ?public?Class?apply(ClassLoader?loader,?Class[]?interfaces)?{
        ????????????Map,?Boolean>?interfaceSet?=?new?IdentityHashMap<>(interfaces.length);
        ????????????//?遍歷你傳入的Class[],我們只傳入了UserInterface.class
        ????????????for?(Class?intf?:?interfaces)?{
        ????????????????Class?interfaceClass?=?null;
        ?????????????????//?獲取接口類
        ????????????????interfaceClass?=?Class.forName(intf.getName(),?false,?loader);
        ?????????????????//?這里就很明確為什么只能傳入接口類,不是接口類會(huì)報(bào)錯(cuò)
        ????????????????if?(!interfaceClass.isInterface())?{
        ????????????????????throw?new?IllegalArgumentException(
        ????????????????????????interfaceClass.getName()?+?"?is?not?an?interface");
        ????????????????}
        ????????????String?proxyPkg?=?null;?
        ????????????int?accessFlags?=?Modifier.PUBLIC?|?Modifier.FINAL;
        ????????????for?(Class?intf?:?interfaces)?{
        ????????????????int?flags?=?intf.getModifiers();
        ????????????????//?驗(yàn)證接口是否是public,不是public代理類會(huì)用接口的package,因?yàn)橹挥性谕话鼉?nèi)才能繼承
        ????????????????//?我們的UserInterface是public,所以跳過
        ????????????????if?(!Modifier.isPublic(flags))?{
        ????????????????????accessFlags?=?Modifier.FINAL;
        ????????????????????String?name?=?intf.getName();
        ????????????????????int?n?=?name.lastIndexOf('.');
        ????????????????????String?pkg?=?((n?==?-1)???""?:?name.substring(0,?n?+?1));
        ????????????????????if?(proxyPkg?==?null)?{
        ????????????????????????proxyPkg?=?pkg;
        ????????????????????}?else?if?(!pkg.equals(proxyPkg))?{
        ????????????????????????throw?new?IllegalArgumentException(
        ????????????????????????????"non-public?interfaces?from?different?packages");
        ????????????????????}
        ????????????????}
        ????????????}
        ?????????//?如果接口類是public,則用默認(rèn)的包
        ????????????if?(proxyPkg?==?null)?{
        ????????????????//?PROXY_PACKAGE?=?"com.sun.proxy";
        ????????????????proxyPkg?=?ReflectUtil.PROXY_PACKAGE?+?".";
        ????????????}
        ?????????//?原子Int,此時(shí)num?=?0
        ????????????long?num?=?nextUniqueNumber.getAndIncrement();
        ????????????// com.sun.proxy.$Proxy0,這里包名和類名就出現(xiàn)了?。?!
        ????????????String?proxyName?=?proxyPkg?+?proxyClassNamePrefix?+?num;
        ?????????//??。。。∩蒫lass文件,查看后面?核心?。。?!
        ????????????byte[]?proxyClassFile?=?ProxyGenerator.generateProxyClass(proxyName,?interfaces,?accessFlags);
        ????????????//??。。】赐晗旅嬖倩貋砜催@行?。。?!
        ????????????//?獲取了字節(jié)數(shù)組之后,獲取了class的二進(jìn)制流將類加載到了JVM中
        ????????????//?并且返回了$Proxy0.class,返回給Factory.get()來包裝
        ????????????return?defineClass0(loader,?proxyName,proxyClassFile,?0,?proxyClassFile.length);
        ???????????
        ????????????}
        ????????}
        ????}

        defineClass0()是Proxy類自定義的類加載的native方法,會(huì)獲取class文件的二進(jìn)制流加載到JVM中,以獲取對應(yīng)的Class對象,這一塊可以參考JVM類加載器。

        class文件寫入本地

        generateProxyClass()方法會(huì)將class二進(jìn)制文件寫入本地目錄,并返回class文件的二進(jìn)制流,使用你傳入的類加載器加載,「這里你知道類加載器的作用了么」。

        ?public?static?byte[]?generateProxyClass(final?String?name,
        ????????????????????????????????????????????Class[]?interfaces)
        ????{
        ????????ProxyGenerator?gen?=?new?ProxyGenerator(name,?interfaces);
        ????????//?生成class文件的二進(jìn)制,查看后面<生成class文件二進(jìn)制>
        ????????final?byte[]?classFile?=?gen.generateClassFile();
        ??????//?將class文件寫入本地??
        ????????if?(saveGeneratedFiles)?{
        ????????????java.security.AccessController.doPrivileged(
        ????????????new?java.security.PrivilegedAction()?{
        ????????????????public?Void?run()?{
        ????????????????????try?{
        ????????????????????????FileOutputStream?file?=
        ????????????????????????????new?FileOutputStream(dotToSlash(name)?+?".class");
        ????????????????????????file.write(classFile);
        ????????????????????????file.close();
        ????????????????????????return?null;
        ????????????????????}?catch?(IOException?e)?{
        ????????????????????????throw?new?InternalError(
        ????????????????????????????"I/O?exception?saving?generated?file:?"?+?e);
        ????????????????????}
        ????????????????}
        ????????????});
        ????????}
        ??????//?返回$Proxy0.class字節(jié)數(shù)組,回到上面
        ????????return?classFile;
        ????}

        生成class文件二進(jìn)制流

        generateClassFile()生成class文件,并存放到字節(jié)數(shù)組,「可以順便學(xué)一下class結(jié)構(gòu),這里也體現(xiàn)了你傳入的class[]的作用」。

        ????private?byte[]?generateClassFile()?{
        ??????//?將hashcode、equals、toString是三個(gè)方法放入代理類中
        ????????addProxyMethod(hashCodeMethod,?Object.class);
        ????????addProxyMethod(equalsMethod,?Object.class);
        ????????addProxyMethod(toStringMethod,?Object.class);
        ????????for?(int?i?=?0;?i?????????????Method[]?methods?=?interfaces[i].getMethods();
        ????????????for?(int?j?=?0;?j??????????????//?將接口類的方法放入新建的代理類中,這里就是sayHello()
        ????????????????addProxyMethod(methods[j],?interfaces[i]);
        ????????????}
        ????????}
        ????????for?(List?sigmethods?:?proxyMethods.values())?{
        ????????????checkReturnTypes(sigmethods);
        ????????}
        ????????//?給代理類增加構(gòu)造方法
        ????????methods.add(generateConstructor());
        ????????for?(List?sigmethods?:?proxyMethods.values())?{
        ????????????for?(ProxyMethod?pm?:?sigmethods)?{
        ???????????????????//?將上面的四個(gè)方法都封裝成Method類型成員變量
        ????????????????????fields.add(new?FieldInfo(pm.methodFieldName,
        ????????????????????????"Ljava/lang/reflect/Method;",
        ?????????????????????????ACC_PRIVATE?|?ACC_STATIC));
        ????????????????????//?generate?code?for?proxy?method?and?add?it
        ????????????????????methods.add(pm.generateMethod());
        ????????????????}
        ????????????}
        ??????//?static靜態(tài)塊構(gòu)造
        ????????methods.add(generateStaticInitializer());
        ????????cp.getClass(dotToSlash(className));
        ????????cp.getClass(superclassName);
        ????????for?(int?i?=?0;?i?????????????cp.getClass(dotToSlash(interfaces[i].getName()));
        ????????}
        ????????cp.setReadOnly();
        ????????ByteArrayOutputStream?bout?=?new?ByteArrayOutputStream();
        ????????DataOutputStream?dout?=?new?DataOutputStream(bout);
        ??????// !!!核心點(diǎn)來了!這里就開始構(gòu)建class文件了,以下都是class的結(jié)構(gòu),只寫一部分
        ????????try?{???
        ????????????// u4 magic,class文件的魔數(shù),確認(rèn)是否為一個(gè)能被JVM接受的class
        ????????????dout.writeInt(0xCAFEBABE);
        ????????????//?u2?minor_version,0
        ????????????dout.writeShort(CLASSFILE_MINOR_VERSION);
        ????????????//?u2?major_version,主版本號,Java8對應(yīng)的是52;
        ????????????dout.writeShort(CLASSFILE_MAJOR_VERSION);
        ????????????//?常量池
        ????????????cp.write(dout);
        ????????????//?其他結(jié)構(gòu),可參考class文件結(jié)構(gòu)
        ????????????dout.writeShort(ACC_PUBLIC?|?ACC_FINAL?|?ACC_SUPER);
        ????????????dout.writeShort(cp.getClass(dotToSlash(className)));
        ????????????dout.writeShort(cp.getClass(superclassName));
        ????????????dout.writeShort(interfaces.length);
        ????????????for?(int?i?=?0;?i?????????????????dout.writeShort(cp.getClass(
        ????????????????????dotToSlash(interfaces[i].getName())));
        ????????????}
        ????????????dout.writeShort(fields.size());
        ????????????for?(FieldInfo?f?:?fields)?{
        ????????????????f.write(dout);
        ????????????}
        ????????????dout.writeShort(methods.size());???????????
        ????????????for?(MethodInfo?m?:?methods)?{
        ????????????????m.write(dout);
        ????????????}
        ????????????dout.writeShort(0);?
        ????????}?catch?(IOException?e)?{
        ????????????throw?new?InternalError("unexpected?I/O?Exception",?e);
        ????????}
        ????????//?將class文件字節(jié)數(shù)組返回
        ????????return?bout.toByteArray();
        ????}

        構(gòu)建$Proxy對象

        newProxyInstance()上半部分經(jīng)過上面層層代碼調(diào)用,獲取了$Proxy0.class,接下來看下部分代碼:

        newInstance

        cl就是上面獲取的Proxy0.class,h就是上面?zhèn)魅氲膗serHandler,被當(dāng)做構(gòu)造參數(shù)來創(chuàng)建$Proxy0對象。然后獲取這個(gè)動(dòng)態(tài)代理對象,調(diào)用sayHello()方法,相當(dāng)于調(diào)用了UserHandler的invoke(),「這里就是UserHandler的作用」!

        $Proxy.class文件

        我們開啟了將代理class寫到本地目錄的功能,在項(xiàng)目下的com/sum/proxy目錄下找到了$Proxy0的class文件。

        「看一下反編譯的class」

        package?com.sun.proxy;

        import?com.test.proxy.UserInterface;
        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?UserInterface?{
        ????private?static?Method?m1;
        ????private?static?Method?m3;
        ????private?static?Method?m2;
        ????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});
        ????????}?catch?(RuntimeException?|?Error?var3)?{
        ????????????throw?var3;
        ????????}?catch?(Throwable?var4)?{
        ????????????throw?new?UndeclaredThrowableException(var4);
        ????????}
        ????}

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

        ????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?int?hashCode()?throws??{
        ????????try?{
        ????????????return?(Integer)super.h.invoke(this,?m0,?(Object[])null);
        ????????}?catch?(RuntimeException?|?Error?var2)?{
        ????????????throw?var2;
        ????????}?catch?(Throwable?var3)?{
        ????????????throw?new?UndeclaredThrowableException(var3);
        ????????}
        ????}

        ????static?{
        ????????try?{
        ????????????m1?=?Class.forName("java.lang.Object").getMethod("equals",?Class.forName("java.lang.Object"));
        ????????????m3?=?Class.forName("com.test.proxy.UserInterface").getMethod("sayHello");
        ????????????m2?=?Class.forName("java.lang.Object").getMethod("toString");
        ????????????m0?=?Class.forName("java.lang.Object").getMethod("hashCode");
        ????????}?catch?(NoSuchMethodException?var2)?{
        ????????????throw?new?NoSuchMethodError(var2.getMessage());
        ????????}?catch?(ClassNotFoundException?var3)?{
        ????????????throw?new?NoClassDefFoundError(var3.getMessage());
        ????????}
        ????}
        }

        結(jié)語

        上面就是動(dòng)態(tài)代理源碼的調(diào)試過程,與之前的猜想的代理類的生成過程比較,動(dòng)態(tài)代理是直接生成class文件,省去了java文件和編譯這一塊。

        剛開始看可能比較繞,跟著注釋及跳轉(zhuǎn)指引,耐心多看兩遍就明白了。動(dòng)態(tài)代理涉及的知識點(diǎn)比較多,我自己看的時(shí)候,在WeakCache這一塊糾結(jié)了一陣,其實(shí)把它當(dāng)成一個(gè)兩層的map對待即可,只不過里面所有的KV都被弱引用包裝。






        關(guān)注Java技術(shù)??锤喔韶?/strong>



        戳原文,獲取精選面試題!
        瀏覽 43
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

          <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            日在线视频| 日韩无码操逼 | 美日韩精品视频 | 欧美老熟妇在线视频 | 扒开美女双腿猛进入喷水视频 | 欧美视频一区二区三区四区 | 国产一级婬乱片免费 | 欧美女人bb图 | 五月天激情久久 | 十大名器全肉爽文小说 |