一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Java之美[從菜鳥到高手演練]之JDK動態(tài)代理的實現(xiàn)及原理

 dawn001 2016-07-18

JDK動態(tài)代理的實現(xiàn)及原理       

作者:二青

郵箱:xtfggef@gmail.com     微博:http://weibo.com/xtfggef

動態(tài)代理,聽上去很高大上的技術(shù),在Java里應(yīng)用廣泛,尤其是在hibernatespring這兩種框架里,在AOP,權(quán)限控制,事務(wù)管理等方面都有動態(tài)代理的實現(xiàn)。JDK本身有實現(xiàn)動態(tài)代理技術(shù),但是略有限制,即被代理的類必須實現(xiàn)某個接口,否則無法使用JDK自帶的動態(tài)代理,因此,如果不滿足條件,就只能使用另一種更加靈活,功能更加強(qiáng)大的動態(tài)代理技術(shù)—— CGLIB。Spring里會自動在JDK的代理和CGLIB之間切換,同時我們也可以強(qiáng)制Spring使用CGLIB。下面我們就動態(tài)代理方面的知識點(diǎn)從頭至尾依次介紹一下。

我們先來看一個例子:

新建一個接口,UserService.java, 只有一個方法add()。

  1. package com.adam.java.basic;  
  2.   
  3. public interface UserService {  
  4.     public abstract void add();  
  5. }  

建一個該接口的實現(xiàn)類UserServiceImpl.java

  1. package com.adam.java.basic;  
  2. public class UserServiceImpl implements UserService {  
  3.   
  4.     @Override  
  5.     public void add() {  
  6.         System.out.println("----- add -----");  
  7.     }  
  8. }  

建一個代理處理類MyInvocationHandler.java

  1. package com.adam.java.basic;  
  2. import java.lang.reflect.InvocationHandler;  
  3. import java.lang.reflect.Method;  
  4. import java.lang.reflect.Proxy;  
  5.   
  6. public class MyInvocationHandler implements InvocationHandler {  
  7.   
  8.     private Object target;  
  9.   
  10.     public MyInvocationHandler(Object target) {  
  11.         super();  
  12.         this.target = target;  
  13.     }  
  14.   
  15.     public Object getProxy() {  
  16.         return Proxy.newProxyInstance(Thread.currentThread()  
  17.                 .getContextClassLoader(), target.getClass().getInterfaces(),  
  18.                 this);  
  19.     }  
  20.   
  21.     @Override  
  22.     public Object invoke(Object proxy, Method method, Object[] args)  
  23.             throws Throwable {  
  24.         System.out.println("----- before -----");  
  25.         Object result = method.invoke(target, args);  
  26.         System.out.println("----- after -----");  
  27.         return result;  
  28.     }  
  29. }  

測試類

  1. package com.adam.java.basic;  
  2. public class DynamicProxyTest {  
  3.   
  4.     public static void main(String[] args) {  
  5.         UserService userService = new UserServiceImpl();  
  6.         MyInvocationHandler invocationHandler = new MyInvocationHandler(  
  7.                 userService);  
  8.   
  9.         UserService proxy = (UserService) invocationHandler.getProxy();  
  10.         proxy.add();  
  11.     }  
  12. }  

執(zhí)行測試類,得到如下輸出:
----- before -----
----- add -----
----- after -----
到這里,我們應(yīng)該會想到點(diǎn)問題:
1. 這個代理對象是由誰且怎么生成的?
2. invoke方法是怎么調(diào)用的?
3. invoke和add方法有什么對應(yīng)關(guān)系?
4. 生成的代理對象是什么樣子的?
帶著這些問題,我們看一下源碼。首先,我們的入口便是上面測試類里的getProxy()方法,我們跟進(jìn)去,看看這個方法:

  1. public Object getProxy() {  
  2.     return Proxy.newProxyInstance(Thread.currentThread()  
  3.             .getContextClassLoader(), target.getClass().getInterfaces(),this);  
  4. }  

也就是說,JDK的動態(tài)代理,是通過一個叫Proxy的類來實現(xiàn)的,我們繼續(xù)跟進(jìn)去,看看Proxy類的newProxyInstance()方法。先來看看JDK的注釋:

[plain] view plain copy
在CODE上查看代碼片派生到我的代碼片
  1. /**  
  2.      * Returns an instance of a proxy class for the specified interfaces  
  3.      * that dispatches method invocations to the specified invocation  
  4.      * handler.  
  5.      *  
  6.      * <p>{@code Proxy.newProxyInstance} throws  
  7.      * {@code IllegalArgumentException} for the same reasons that  
  8.      * {@code Proxy.getProxyClass} does.  
  9.      *  
  10.      * @param   loader the class loader to define the proxy class  
  11.      * @param   interfaces the list of interfaces for the proxy class  
  12.      *          to implement  
  13.      * @param   h the invocation handler to dispatch method invocations to  
  14.      * @return  a proxy instance with the specified invocation handler of a  
  15.      *          proxy class that is defined by the specified class loader  
  16.      *          and that implements the specified interfaces  

根據(jù)JDK注釋我們得知,newProxyInstance方法最終將返回一個實現(xiàn)了指定接口的類的實例,其三個參數(shù)分別是:ClassLoader,指定的接口及我們自己定義的InvocationHandler類。我摘幾條關(guān)鍵的代碼出來,看看這個代理類的實例對象到底是怎么生成的。

  1. Class<?> cl = getProxyClass0(loader, intfs);  
  2. ...  
  3. final Constructor<?> cons = cl.getConstructor(constructorParams);  
  4. ...  
  5. return cons.newInstance(new Object[]{h});  

有興趣的同學(xué)可以自己看看JDK的源碼,當(dāng)前我用的版本是JDK1.8.25,每個版本實現(xiàn)方式可能會不一樣,但基本一致,請研究源碼的同學(xué)注意這一點(diǎn)。上面的代碼表明,首先通過getProxyClass獲得這個代理類,然后通過c1.getConstructor()拿到構(gòu)造函數(shù),最后一步,通過cons.newInstance返回這個新的代理類的一個實例,注意:調(diào)用newInstance的時候,傳入的參數(shù)為h,即我們自己定義好的InvocationHandler類,先記著這一步,后面我們就知道這里這樣做的原因。

其實這三條代碼,核心就是這個getProxyClass方法,另外兩行代碼是Java反射的應(yīng)用,和我們當(dāng)前的興趣點(diǎn)沒什么關(guān)系,所以我們繼續(xù)研究這個getProxyClass方法。這個方法,注釋很簡單,如下:

  1. /* 
  2.          * Look up or generate the designated proxy class. 
  3.          */  
  4.         Class<?> cl = getProxyClass0(loader, intfs);  

就是生成這個關(guān)鍵的代理類,我們跟進(jìn)去看一下。

  1. private static Class<?> getProxyClass0(ClassLoader loader,  
  2.                                            Class<?>... interfaces) {  
  3.         if (interfaces.length > 65535) {  
  4.             throw new IllegalArgumentException("interface limit exceeded");  
  5.         }  
  6.   
  7.         // If the proxy class defined by the given loader implementing  
  8.         // the given interfaces exists, this will simply return the cached copy;  
  9.         // otherwise, it will create the proxy class via the ProxyClassFactory  
  10.         return proxyClassCache.get(loader, interfaces);  
  11.     }  

這里用到了緩存,先從緩存里查一下,如果存在,直接返回,不存在就新創(chuàng)建。在這個get方法里,我們看到了如下代碼:
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
此處提到了apply(),是Proxy類的內(nèi)部類ProxyClassFactory實現(xiàn)其接口的一個方法,具體實現(xiàn)如下:

  1. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  
  2.   
  3.             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);  
  4.             for (Class<?> intf : interfaces) {  
  5.                 /* 
  6.                  * Verify that the class loader resolves the name of this 
  7.                  * interface to the same Class object. 
  8.                  */  
  9.                 Class<?> interfaceClass = null;  
  10.                 try {  
  11.                     interfaceClass = Class.forName(intf.getName(), false, loader);  
  12.                 } catch (ClassNotFoundException e) {  
  13.                 }  
  14.                 if (interfaceClass != intf) {  
  15.                     throw new IllegalArgumentException(  
  16.                         intf + " is not visible from class loader");  
  17.                 }...  

看到Class.forName()的時候,我想大多數(shù)人會笑了,終于看到熟悉的方法了,沒錯!這個地方就是要加載指定的接口,既然是生成類,那就要有對應(yīng)的class字節(jié)碼,我們繼續(xù)往下看:

  1. /* 
  2.  * Generate the specified proxy class. 
  3.  */  
  4.    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
  5.    proxyName, interfaces, accessFlags);  
  6.     try {  
  7.           return defineClass0(loader, proxyName,  
  8.           proxyClassFile, 0, proxyClassFile.length);  

這段代碼就是利用ProxyGenerator為我們生成了最終代理類的字節(jié)碼文件,即getProxyClass0()方法的最終返回值。所以讓我們回顧一下最初的四個問題:

1. 這個代理對象是由誰且怎么生成的?

2. invoke方法是怎么調(diào)用的?

3. invoke和add方法有什么對應(yīng)關(guān)系?

4. 生成的代理對象是什么樣子的?

對于第一個問題,我想答案已經(jīng)清楚了,我再屢一下思路:由Proxy類的getProxyClass0()方法生成目標(biāo)代理類,然后拿到該類的構(gòu)造方法,最后通過反射的newInstance方法,產(chǎn)生代理類的實例對象。

接下來,我們看看其他的三個方法,我想先從第四個入手,因為有了上面的生成字節(jié)碼的代碼,那我們可以模仿這一步,自己生成字節(jié)碼文件看看,所以,我用如下代碼,生成了這個最終的代理類。

  1. package com.adam.java.basic;  
  2.   
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import sun.misc.ProxyGenerator;  
  6.   
  7. public class DynamicProxyTest {  
  8.   
  9.     public static void main(String[] args) {  
  10.         UserService userService = new UserServiceImpl();  
  11.         MyInvocationHandler invocationHandler = new MyInvocationHandler(  
  12.                 userService);  
  13.   
  14.         UserService proxy = (UserService) invocationHandler.getProxy();  
  15.         proxy.add();  
  16.           
  17.         String path = "C:/$Proxy0.class";  
  18.         byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",  
  19.                 UserServiceImpl.class.getInterfaces());  
  20.         FileOutputStream out = null;  
  21.   
  22.         try {  
  23.             out = new FileOutputStream(path);  
  24.             out.write(classFile);  
  25.             out.flush();  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         } finally {  
  29.             try {  
  30.                 out.close();  
  31.             } catch (IOException e) {  
  32.                 e.printStackTrace();  
  33.             }  
  34.         }  
  35.     }  
  36. }  

上面測試方法里的proxy.add(),此處的add()方法,就已經(jīng)不是原始的UserService里的add()方法了,而是新生成的代理類的add()方法,我們將生成的$Proxy0.class文件用jd-gui打開,我去掉了一些代碼,add()方法如下:

  1. public final void add()  
  2.     throws   
  3.   {  
  4.     try  
  5.     {  
  6.       this.h.invoke(this, m3, null);  
  7.       return;  
  8.     }  
  9.     catch (Error|RuntimeException localError)  
  10.     {  
  11.       throw localError;  
  12.     }  
  13.     catch (Throwable localThrowable)  
  14.     {  
  15.       throw new UndeclaredThrowableException(localThrowable);  
  16.     }  
  17.   }  

核心就在于this.h.invoke(this. m3, null);此處的h是啥呢?我們看看這個類的類名:

public final class $Proxy0 extends Proxy implements UserService

不難發(fā)現(xiàn),新生成的這個類,繼承了Proxy類實現(xiàn)了UserService這個方法,而這個UserService就是我們指定的接口,所以,這里我們基本可以斷定,JDK的動態(tài)代理,生成的新代理類就是繼承了Proxy基類,實現(xiàn)了傳入的接口的類。那這個h到底是啥呢?我們再看看這個新代理類,看看構(gòu)造函數(shù):

  1. public $Proxy0(InvocationHandler paramInvocationHandler)  
  2.     throws   
  3.   {  
  4.     super(paramInvocationHandler);  
  5.   }  

構(gòu)造函數(shù)里傳入了一個InvocationHandler類型的參數(shù),看到這里,我們就應(yīng)該想到之前的一行代碼:

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

這是newInstance方法的最后一句,傳入的h,就是這里用到的h,也就是我們最初自己定義的MyInvocationHandler類的實例。所以,我們發(fā)現(xiàn),其實最后調(diào)用的add()方法,其實調(diào)用的是MyInvocationHandler的invoke()方法。我們再來看一下這個方法,找一下m3的含義,繼續(xù)看代理類的源碼:

  1. static  
  2.   {  
  3.     try  
  4.     {  
  5.       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });  
  6.       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
  7.       m3 = Class.forName("com.adam.java.basic.UserService").getMethod("add", new Class[0]);  
  8.       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
  9.       return;  
  10.     }  

驚喜的發(fā)現(xiàn),原來這個m3,就是原接口的add()方法,看到這里,還有什么不明白的呢?我想2,3,4問題都應(yīng)該迎刃而解了吧?我們繼續(xù),看看原始MyInvocationHandler里的invoke()方法:

  1. <span style="white-space:pre">  </span>@Override  
  2.     public Object invoke(Object proxy, Method method, Object[] args)  
  3.             throws Throwable {  
  4.         System.out.println("----- before -----");  
  5.         Object result = method.invoke(target, args);  
  6.         System.out.println("----- after -----");  
  7.         return result;  
  8.     }  

m3就是將要傳入的method,所以,為什么先輸出before,后輸出after,到這里是不是全明白了呢?這,就是JDK的動態(tài)代理整個過程,不難吧?

最后,我稍微總結(jié)一下JDK動態(tài)代理的操作過程:

1. 定義一個接口,該接口里有需要實現(xiàn)的方法,并且編寫實際的實現(xiàn)類。

2. 定義一個InvocationHandler類,實現(xiàn)InvocationHandler接口,重寫invoke()方法,且添加getProxy()方法。

總結(jié)一下動態(tài)代理實現(xiàn)過程:

1. 通過getProxyClass0()生成代理類。

2. 通過Proxy.newProxyInstance()生成代理類的實例對象,創(chuàng)建對象時傳入InvocationHandler類型的實例。

3. 調(diào)用新實例的方法,即此例中的add(),即原InvocationHandler類中的invoke()方法。

好了,寫了這么多,也該結(jié)尾了,感謝博友Rejoy的一篇文章,讓我有了參考。同時歡迎大家一起提問討論,如有問題,請留言,我會抽空回復(fù)。相關(guān)代碼已經(jīng)上傳至百度網(wǎng)盤,下載地址。

聯(lián)系方式:

郵箱:xtfggef@gmail.com     

微博:http://weibo.com/xtfggef


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产不卡视频一区在线| 91欧美激情在线视频| 亚洲天堂一区在线播放| 国产女同精品一区二区| 日本加勒比在线观看不卡| 欧美一级黄片免费视频| 麻豆果冻传媒一二三区| 国产日产欧美精品视频| 久久经典一区二区三区| 国产精品99一区二区三区| 亚洲黄色在线观看免费高清| 婷婷开心五月亚洲综合| 国产精品福利一二三区| 亚洲av熟女一区二区三区蜜桃| 国产精品久久三级精品| 欧美精品亚洲精品一区| 亚洲中文字幕亲近伦片| 欧美一区二区在线日韩| 亚洲中文字幕人妻系列| 国产精品久久久久久久久久久痴汉| 成年人黄片大全在线观看| 欧美日韩亚洲国产av| 亚洲精品中文字幕一二三| 国产免费自拍黄片免费看| 好吊日成人免费视频公开| 亚洲黄色在线观看免费高清| 四季精品人妻av一区二区三区| 欧美午夜一级艳片免费看| 欧美av人人妻av人人爽蜜桃| 日韩人妻av中文字幕| 国产精品自拍杆香蕉视频| 久草精品视频精品视频精品 | 日本少妇aa特黄大片| 国产日韩欧美一区二区| 亚洲av日韩av高潮无打码| 日本欧美视频在线观看免费 | 五月婷婷欧美中文字幕| 日本加勒比在线播放一区| 亚洲熟妇熟女久久精品| 久久成人国产欧美精品一区二区| 精品推荐国产麻豆剧传媒|