<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從源碼的角度搞懂 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)的知識(shí),遇到了一個(gè)動(dòng)態(tài)代理的問(wèn)題,然后就決定探究一下動(dòng)態(tài)代理。

          這里先科普一下RMI。

          RMI

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

          接口定義

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

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

          遠(yuǎn)程對(duì)象定義

          定義一個(gè)遠(yuǎn)程對(duì)象類,繼承UnicastRemoteObject來(lái)實(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對(duì)象在注冊(cè)表上進(jìn)行注冊(cè)。

          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ù)端注冊(cè)表獲取遠(yuǎn)程對(duì)象,在服務(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è)簡(jiǎn)單的RMI demo完成。

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

          提出問(wèn)題

          看了看RMI代碼,覺(jué)得UserInterface這個(gè)接口有點(diǎn)多余,如果客戶端使用Naming.lookup()獲取的對(duì)象不強(qiáng)轉(zhuǎn)成UserInterface,直接強(qiáng)轉(zhuǎn)成User是不是也可以,于是試了一下,就報(bào)了以下錯(cuò)誤:似曾相識(shí)又有點(diǎn)陌生的$Proxy0,翻了翻塵封的筆記找到了是動(dòng)態(tài)代理的知識(shí)點(diǎn),寥寥幾筆帶過(guò),所以決定梳理一下動(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í)角色綁定的處理類對(duì)象的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);
          ????????//?開(kāi)啟將代理類class文件保存到本地模式,平時(shí)可以省略
          ????????System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles",?"true");
          ????????//?動(dòng)態(tài)代理生成代理對(duì)象$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é)完了,可是還有好多問(wèn)題不明白。

          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ì)模式的代理模式有相同之處呢?

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

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

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

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

          接著對(duì)比一下兩者的處理類和代理類,發(fā)現(xiàn)UserHandler的invoke()和UserProxy的sayHello()這兩個(gè)方法的代碼都是一樣的。那么,是不是新建一個(gè)UserProxy類,然后實(shí)現(xiàn)UserInterface接口并持有UserHandler的對(duì)象,在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{
          ???//?持有處理類的對(duì)象
          ????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ù)來(lái)動(dòng)態(tài)生成這個(gè)代理類代碼,如果讓我實(shí)現(xiàn),會(huì)是以下這個(gè)流程。

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

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


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

          擁抱源碼

          調(diào)用流程圖

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

          流程圖

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

          newProxyInstance()

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

          「上部分代碼」

          newProxyInstance()

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

          getProxyClass0()

          getProxyClass()

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

          WeakCache

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

          弱引用

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

          舉個(gè)栗子:

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

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

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

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

          WeakCache的get()

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

          ?public?V?get(K?key,?P?parameter)?{
          ????????Objects.requireNonNull(parameter);
          ????????//?遍歷refQueue,然后將緩存map中對(duì)應(yīng)的失效value刪除
          ????????expungeStaleEntries();
          ????????//?以ClassLoader為key,構(gòu)建map的一級(jí)緩存的Key,是CacheKey對(duì)象
          ????????Object?cacheKey?=?CacheK.valueOf(key,?refQueue);
          ????????//?通過(guò)Key從map中獲取一級(jí)緩存的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;
          ????????????}
          ????????}
          ????????//?生成二級(jí)緩存的subKey,現(xiàn)在緩存map的結(jié)構(gòu)就是ConCurrentMap>
          ????????//?看后面的<生成二級(jí)緩存Key>!??!
          ????????Object?subKey?=?Objects.requireNonNull(subKeyFactory.apply(key,?parameter));
          ????????//?根據(jù)二級(jí)緩存的subKey獲取value
          ????????Supplier?supplier?=?valuesMap.get(subKey);
          ????????Factory?factory?=?null;
          ????????
          //?!??!直到完成二級(jí)緩存Value的構(gòu)建才結(jié)束,Value是弱引用的$Proxy0.class?。?!
          ????????while?(true)?{
          ???????????//?第一次循環(huán):suppiler肯定是null,因?yàn)檫€沒(méi)有將放入二級(jí)緩存的KV值
          ???????????//?第二次循環(huán):這里suppiler不為null了?。?!進(jìn)入if
          ????????????if?(supplier?!=?null)?{
          ????????????????//?第二次循環(huán):真正生成代理對(duì)象,
          ????????????????//?往后翻,看<生成二級(jí)緩存Value>,核心!!!!!
          ????????????????//?看完后面回到這里:value就是弱引用后的$Proxy0.class
          ????????????????V?value?=?supplier.get();
          ????????????????if?(value?!=?null)?{
          ?????????????//?本方法及上部分的最后一行代碼,跳轉(zhuǎn)最后的<構(gòu)建$Proxy對(duì)象>
          ????????????????????return?value;
          ????????????????}
          ????????????}
          ??????????//?第一次循環(huán):factory肯定為null,生成二級(jí)緩存的Value
          ????????????if?(factory?==?null)?{
          ????????????????factory?=?new?Factory(key,?parameter,?subKey,?valuesMap);
          ????????????}
          ?????????//?第一次循環(huán):將subKey和factory作為KV放入二級(jí)緩存
          ????????????if?(supplier?==?null)?{
          ????????????????supplier?=?valuesMap.putIfAbsent(subKey,?factory);
          ????????????????if?(supplier?==?null)?{
          ????????????????????//?第一次循環(huán):賦值之后suppiler就不為空了,記?。。。。。?/span>
          ????????????????????supplier?=?factory;
          ????????????????}
          ????????????}?
          ???????????}
          ????????}
          ????}

          生成二級(jí)緩存Key

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

          KeyFactory.apply()

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

          Key1

          生成二級(jí)緩存Value

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

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

          別回頭,接著往下看。

          Factory.get()生成弱引用value

          「CacheValue」類是一個(gè)弱引用,是二級(jí)緩存的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對(duì)象包裝成弱引用
          ????????????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,所以跳過(guò)
          ????????????????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;
          ?????????//??。。?!生成class文件,查看后面?核心?。。?!
          ????????????byte[]?proxyClassFile?=?ProxyGenerator.generateProxyClass(proxyName,?interfaces,?accessFlags);
          ????????????//?!??!看完下面再回來(lái)看這行!?。?!
          ????????????//?獲取了字節(jié)數(shù)組之后,獲取了class的二進(jìn)制流將類加載到了JVM中
          ????????????//?并且返回了$Proxy0.class,返回給Factory.get()來(lái)包裝
          ????????????return?defineClass0(loader,?proxyName,proxyClassFile,?0,?proxyClassFile.length);
          ???????????
          ????????????}
          ????????}
          ????}

          defineClass0()是Proxy類自定義的類加載的native方法,會(huì)獲取class文件的二進(jìn)制流加載到JVM中,以獲取對(duì)應(yīng)的Class對(duì)象,這一塊可以參考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)來(lái)了!這里就開(kāi)始構(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,主版本號(hào),Java8對(duì)應(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對(duì)象

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

          newInstance

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

          $Proxy.class文件

          我們開(kāi)啟了將代理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é)語(yǔ)

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

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






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



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

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  伊人婷婷 | 黑人巨大两根挤进一个 | av在线地址 | 日韩一级免费观看 | AV女人天堂 |