亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

自己寫一個java.lang.reflect.Proxy代理的實現

2019-11-14 15:16:01
字體:
來源:轉載
供稿:網友

前言

java設計模式9:代理模式一文中,講到了動態代理,動態代理里面用到了一個類就是java.lang.reflect.PRoxy,這個類是根據代理內容為傳入的接口生成代理用的。本文就自己寫一個Proxy類出來,功能和java.lang.reflect.Proxy一樣,傳入接口、代理內容,生成代理。

拋磚引玉吧,個人覺得自己寫一些JDK里面的那些類挺好的,寫一遍和看一遍真的是兩個不同的概念,寫一遍既加深了對于這些類的理解、提升了自己的寫代碼水平,也可以在寫完之后對比一下自己的實現有哪些寫得不好、又有哪些沒考慮到的地方,這樣可以顯著地提高自己,像我就自己寫過JDK里面主要的集合類、工具類、String里面常用方法等。

本文的代碼基礎來源于馬士兵Proxy的視頻(順便說一句,個人覺得馬士兵的視頻講得比較拖拉,但是關于一些原理性、偏底層的東西講得還蠻好的),一共分三個版本。可能有人覺得,人家視頻上的內容拿過來寫個文章,有意思嗎?真不是,我是這么認為的:

1、把別人的東西變成自己的東西是一個過程,盡管代碼是基于馬士兵Proxy的視頻的,但是所有的代碼都是在自己這里手打、運行通過并自己充分理解了的,把別人的東西不加思考地復制黏貼沒有意義,但是把別人的知識變成自己的理解并分享我覺得是一件好事

2、代碼盡管基于馬士兵Proxy的基礎上,但在這個基礎上也是做了自己的優化過的

 

動態代理的實現應用到的技術

1、動態編譯技術,可以使用Java自帶的JavaCompiler類,也可以使用CGLIB、ASM等字節碼增強技術,Java的動態代理包括Spring的內部實現貌似用的都是這個

2、反射,包括對于類.class和getClass()方法的理解,Method類、Constructor類的理解

3、IO流,主要就是字符輸出流FileWriter

4、對于ClassLoader的理解

 

基礎類

先把基礎類定義在這兒,首先是一個HelloWorld接口:

public interface HelloWorld{    void print();}

HelloWorld接口的實現類:

public class HelloWorldImpl implements HelloWorld{    public void print()    {        System.out.println("Hello World");    }}

為這個接口寫一個簡單的靜態代理類:

public class StaticProxy implements HelloWorld{    private HelloWorld helloWorld;        public StaticProxy(HelloWorld helloWorld)    {        this.helloWorld = helloWorld;    }        public void print()    {        System.out.println("Before Hello World!");        helloWorld.print();        System.out.println("After Hello World!");    }}

 

版本1:為一個靜態代理動態生成一個代理類

我們知道如果用靜態代理的話,那么每個接口都要為之寫一個.java的代理類,這樣就可能造成代理類無限膨脹,如果可以讓Java幫我們自動生成一個就好了,不過還真的可以,看下第一個版本的代碼:

 1 public class ProxyVersion_0 implements Serializable 2 { 3     private static final long serialVersionUID = 1L; 4      5     public static Object newProxyInstance() throws Exception 6     { 7         String src = "package com.xrq.proxy;/n/n" +  8                      "public class StaticProxy implements HelloWorld/n" +  9                      "{/n" + 10                      "/tHelloWorld helloWorld;/n/n" + 11                      "/tpublic StaticProxy(HelloWorld helloWorld)/n" + 12                      "/t{/n" + 13                      "/t/tthis.helloWorld = helloWorld;/n" + 14                      "/t}/n/n" + 15                      "/tpublic void print()/n" + 16                      "/t{/n" + 17                      "/t/tSystem.out.println(/"Before Hello World!/");/n" + 18                      "/t/thelloWorld.print();/n" + 19                      "/t/tSystem.out.println(/"After Hello World!/");/n" + 20                      "/t}/n" + 21                      "}";22         23         /** 生成一段Java代碼 */24         String fileDir = System.getProperty("user.dir");25         String fileName = fileDir + "http://src//com//xrq//proxy//StaticProxy.java";26         File javaFile = new File(fileName);27         Writer writer = new FileWriter(javaFile);28         writer.write(src);29         writer.close();30         31         /** 動態編譯這段Java代碼,生成.class文件 */32         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();33         StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);34         Iterable<? extends JavaFileObject> iter = sjfm.getJavaFileObjects(fileName);35         CompilationTask ct = compiler.getTask(null, sjfm, null, null, null, iter);36         ct.call();37         sjfm.close();38         39         /** 將生成的.class文件載入內存,默認的ClassLoader只能載入CLASSPATH下的.class文件 */40         URL[] urls = new URL[] {(new URL("file://" + System.getProperty("user.dir") + "http://src"))};41         URLClassLoader ul = new URLClassLoader(urls);42         Class<?> c = ul.loadClass("com.xrq.proxy.StaticProxy");43         44         /** 利用反射將c實例化出來 */45         Constructor<?> constructor = c.getConstructor(HelloWorld.class);46         HelloWorld helloWorldImpl = new HelloWorldImpl();47         HelloWorld helloWorld = (HelloWorld)constructor.newInstance(helloWorldImpl);48         49         /** 使用完畢刪除生成的代理.java文件和.class文件,這樣就看不到動態生成的內容了 */50         File classFile = new File(fileDir + "http://src//com//xrq//proxy//StaticProxy.class");51         javaFile.delete();52         classFile.delete();53         54         return helloWorld;55     }56 }

每一步的注釋都在上面了,解釋一下大致思路:

1、我們在另外一個類里面自己拼一段靜態代理的代碼的字符串

2、為這個字符串生成一個.java文件,并放在我們工程的某個目錄下面,因為是.java文件,所以在src下

3、利用JavaCompiler類動態編譯這段.java代碼使之被編譯成一個.class文件,JavaCompiler不熟悉沒關系,知道就好了

4、因為在src下生成編譯之后的.java文件,而默認的ClassLoader只能加載CLASSPATH下的.class文件,所以用URLClassLoader

5、由于代理類只有一個帶參數的構造方法,所以要用java.lang.reflect.Constructor

6、最后把生成的StaticProxy.class文件刪除(最好生成的StaticProxy.java也刪除,這里沒刪除,是因為StaticProxy是生成的一個重要的中間類,功能都在它這兒,所以不刪,出了錯都要靠看這個類來定位問題的),這樣代理的中間內容都沒了,把反射newInstance()出來的內容返回出去就大功告成了

可以自己看一下生成的StaticProxy.java對不對,寫一段代碼測試一下:

public static void main(String[] args) throws Exception{        long start = System.currentTimeMillis();    HelloWorld helloWorld = (HelloWorld)ProxyVersion_0.newProxyInstance();    System.out.println("動態生成代理耗時:" + (System.currentTimeMillis() - start) + "ms");    helloWorld.print();    System.out.println();        }

結果為:

動態生成代理耗時:387msBefore Hello World!Hello WorldAfter Hello World!

沒有問題??赡苡行┤诉\行會報錯"Exception in thread "main" java.lang.ClassNotFoundException: com.xrq.proxy.StaticProxy",沒關系,那是因為雖然你的src目錄下生成了StaticProxy.class,但沒有出來,點擊src文件夾,再按F5(或者右鍵,點擊Refresh也行)刷新一下就可以了

 

版本二:為指定接口生成代理類

版本一已經實現了動態生成一個代理的.class文件了,算是成功的第一步,接下來要做進一步的改進。版本一只可以為固定的一個接口生成代理,現在改進成,傳入某個接口的java.lang.Class對象,可以為這個接口及里面的方法都生成代理內容,代碼這么寫:

 1 public class ProxyVersion_1 implements Serializable 2 { 3     private static final long serialVersionUID = 1L; 4      5     public static Object newProxyInstance(Class<?> interfaces) throws Exception 6     { 7         Method[] methods = interfaces.getMethods(); 8          9         StringBuilder sb = new StringBuilder(700);10         11         sb.append("package com.xrq.proxy;/n/n");12         sb.append("public class StaticProxy implements " +  interfaces.getSimpleName() + "/n");13         sb.append("{/n");14         sb.append("/t" + interfaces.getSimpleName() + " interfaces;/n/n");15         sb.append("/tpublic StaticProxy(" + interfaces.getSimpleName() +  " interfaces)/n");16         sb.append("/t{/n");17         sb.append("/t/tthis.interfaces = interfaces;/n");18         sb.append("/t}/n/n");19         for (Method m : methods)20         {21             sb.append("/tpublic " + m.getReturnType() + " " + m.getName() + "()/n");22             sb.append("/t{/n");23             sb.append("/t/tSystem.out.println(/"Before Hello World!/");/n");24             sb.append("/t/tinterfaces." + m.getName() + "();/n");25             sb.append("/t/tSystem.out.println(/"After Hello World!/");/n");26             sb.append("/t}/n");27         }28         sb.append("}");29         30         /** 生成一段Java代碼 */31         String fileDir = System.getProperty("user.dir");32         String fileName = fileDir + "http://src//com//xrq//proxy//StaticProxy.java";33         File javaFile = new File(fileName);34         Writer writer = new FileWriter(javaFile);35         writer.write(sb.toString());36         writer.close();37         38         /** 動態編譯這段Java代碼,生成.class文件 */39         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();40         StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);41         Iterable<? extends JavaFileObject> iter = sjfm.getJavaFileObjects(fileName);42         CompilationTask ct = compiler.getTask(null, sjfm, null, null, null, iter);43         ct.call();44         sjfm.close();45         46         /** 將生成的.class文件載入內存,默認的ClassLoader只能載入CLASSPATH下的.class文件 */47         URL[] urls = new URL[] {(new URL("file://" + System.getProperty("user.dir") + "http://src"))};48         URLClassLoader ul = new URLClassLoader(urls);49         Class<?> c = ul.loadClass("com.xrq.proxy.StaticProxy");50         51         /** 利用反射將c實例化出來 */52         Constructor<?> constructor = c.getConstructor(HelloWorld.class);53         HelloWorld helloWorldImpl = new HelloWorldImpl();54         Object obj = constructor.newInstance(helloWorldImpl);55         56         /** 使用完畢刪除生成的代理.java文件和.class文件,這樣就看不到動態生成的內容了 */57         /*File classFile = new File(fileDir + "http://src//com//xrq//proxy//StaticProxy.class");58         javaFile.delete();59         classFile.delete();*/60         61         return obj;62     }63 }

看到下面都沒有變化,變化的地方就是在生成StaticProxy.java的地方,通過反射獲取接口及方法的信息,這個版本的改進應該很好理解,寫一段代碼測試一下:

public static void main(String[] args) throws Exception{        long start = System.currentTimeMillis();    HelloWorld helloWorld = (HelloWorld)ProxyVersion_1.newProxyInstance(HelloWorld.class);    System.out.println("動態生成代理耗時:" + (System.currentTimeMillis() - start) + "ms");    helloWorld.print();    System.out.println();}

運行結果為:

動態生成代理耗時:389msBefore Hello World!Hello WorldAfter Hello World!

也沒有問題

 

版本三:讓代理內容可復用

接下來要到最后一個版本了,版本二解決的問題是可以為任何接口生成代理,那最后一個版本要解決的問題自然是可以為任何接口生成任何代理的問題了,首先定義一個接口InvocationHandler,這么起名字是因為JDK提供的代理實例處理程序的接口也是InvocationHandler:

public interface InvocationHandler{    void invoke(Object proxy, Method method) throws Exception;}

所以我們的Proxy類也要修改了,改為:

 1 public class ProxyVersion_2 implements Serializable 2 { 3     private static final long serialVersionUID = 1L; 4      5     public static Object newProxyInstance(Class<?> interfaces, InvocationHandler h) throws Exception 6     { 7         Method[] methods = interfaces.getMethods();         8         StringBuilder sb = new StringBuilder(1024); 9         10         sb.append("package com.xrq.proxy;/n/n");11         sb.append("import java.lang.reflect.Method;/n/n");12         sb.append("public class $Proxy1 implements " +  interfaces.getSimpleName() + "/n");13         sb.append("{/n");14         sb.append("/tInvocationHandler h;/n/n");15         sb.append("/tpublic $Proxy1(InvocationHandler h)/n");16         sb.append("/t{/n");17         sb.append("/t/tthis.h = h;/n");18         sb.append("/t}/n/n");19         for (Method m : methods)20         {21             sb.append("/tpublic " + m.getReturnType() + " " + m.getName() + "()/n");22             sb.append("/t{/n");23             sb.append("/t/ttry/n");24             sb.append("/t/t{/n");25             sb.append("/t/t/tMethod md = " + interfaces.getName() + ".class.getMethod(/"" + m.getName() + "/");/n");26             sb.append("/t/t/th.invoke(this, md);/n");27             sb.append("/t/t}/n");28             sb.append("/t/tcatch (Exception e)/n");29             sb.append("/t/t{/n");30             sb.append("/t/t/te.printStackTrace();/n");31             sb.append("/t/t}/n");32             sb.append("/t}/n");33         }34         sb.append("}");35         36         /** 生成一段Java代碼 */37         String fileDir = System.getProperty("user.dir");38         String fileName = fileDir + "http://src//com//xrq//proxy//$Proxy1.java";39         File javaFile = new File(fileName);40         Writer writer = new FileWriter(javaFile);41         writer.write(sb.toString());42         writer.close();43         44         /** 動態編譯這段Java代碼,生成.class文件 */45         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();46         StandardJavaFileManager sjfm = compiler.getStandardFileManager(null, null, null);47         Iterable<? extends JavaFileObject> iter = sjfm.getJavaFileObjects(fileName);48         CompilationTask ct = compiler.getTask(null, sjfm, null, null, null, iter);49         ct.call();50         sjfm.close();51         52         /** 將生成的.class文件載入內存,默認的ClassLoader只能載入CLASSPATH下的.class文件 */53         URL[] urls = new URL[] {(new URL("file://" + System.getProperty("user.dir") + "http://src"))};54         URLClassLoader ul = new URLClassLoader(urls);55         Class<?> c = Class.forName("com.xrq.proxy.$Proxy1", false, ul);56         57         /** 利用反射將c實例化出來 */58         Constructor<?> constructor = c.getConstructor(InvocationHandler.class);59         Object obj = constructor.newInstance(h);60         61         /** 使用完畢刪除生成的代理.java文件和.class文件,這樣就看不到動態生成的內容了 */62         File classFile = new File(fileDir + "http://src//com//xrq//proxy//$Proxy1.class");63         javaFile.delete();64         classFile.delete();65         66         return obj;67     }68 }

最明顯的變化,代理的名字變了,從StaticProxy變成了$Proxy1,因為JDK也是這么命名的,用過代理的應該有印象。這個改進中拼接$Proxy1的.java文件是一個難點,不過我覺得可以不用糾結在這里,關注重點,看一下生成的$Proxy1.java的內容是什么:

public class $Proxy1 implements HelloWorld{    InvocationHandler h;    public $Proxy1(InvocationHandler h)    {        this.h = h;    }    public void print()    {        try        {            Method md = com.xrq.proxy.HelloWorld.class.getMethod("print");            h.invoke(this, md);        }        catch (Exception e)        {            e.printStackTrace();        }    }}

看到,我們把對于待生成代理的接口方法的調用,變成了對于InvocationHandler接口實現類的invoke方法的調用(這就是動態代理最關鍵的一點),并傳入了待調用的接口方法,這樣不就實現了我們的要求了嗎?我們InvocationHandler接口的實現類寫invoke方法的具體實現,傳入的第二個參數md.invoke就是調用被代理對象的方法,在這個方法前后都是代理內容,想加什么加什么,不就實現了動態代理了?所以,我們看一個InvocationHandler實現類的寫法:

public class HelloInvocationHandler implements InvocationHandler{    private Object obj;        public HelloInvocationHandler(Object obj)    {        this.obj = obj;    }        public void invoke(Object proxy, Method method)    {        System.out.println("Before Hello World!");        try        {            method.invoke(obj, new Object[]{});        }         catch (Exception e)        {            e.printStackTrace();        }        System.out.println("After Hello World!");    }}

寫個main函數測試一下:

public static void main(String[] args) throws Exception{        long start = System.currentTimeMillis();    HelloWorld helloWorldImpl = new HelloWorldImpl();    InvocationHandler ih = new HelloInvocationHandler(helloWorldImpl);    HelloWorld helloWorld = (HelloWorld)ProxyVersion_2.newProxyInstance(HelloWorld.class, ih);    System.out.println("動態生成代理耗時:" + (System.currentTimeMillis() - start) + "ms");    helloWorld.print();    System.out.println();}

運行結果為:

動態生成代理耗時:351msBefore Hello World!Hello WorldAfter Hello World!

沒有問題

 

后記

雖然我們自己寫了Proxy,但是JDK絕對不會用這種方式實現,原因無他,就是太慢。看到三個版本的代碼,運行時間都在300ms以上,效率如此低的實現,如何能給開發者使用?我拿JDK提供的Proxy和InvocationHandler自己寫了一個簡單的動態代理,耗時基本只在5ms左右。所以,文章的內容僅供學習、研究,知識點很多,如果能把這篇文章里面的東西都弄懂,對于個人水平、對于Java很多知識點的理解,絕對是一個非常大的提高。

 


上一篇:Java_反射_基礎

下一篇:初深線程

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲图中文字幕| 久久国产加勒比精品无码| 国产精品久久国产精品99gif| 久久精品人人爽| 国产精品99久久久久久久久久久久| 日韩中文字幕在线看| 欧美大学生性色视频| 91精品国产91久久久久久不卡| 亚洲国产一区二区三区四区| 欧美激情伊人电影| 国产精品视频成人| 国产精品美女主播在线观看纯欲| 九九视频这里只有精品| 国产欧美一区二区三区久久| 国产欧美欧洲在线观看| 日韩高清a**址| 色妞久久福利网| 成人精品一区二区三区电影免费| 亚州精品天堂中文字幕| 深夜福利亚洲导航| 欧美激情精品久久久久久| 亚洲精品720p| 欧美成年人网站| 日韩欧美国产免费播放| 国产一区二区黄| 久久成人综合视频| 色综合伊人色综合网站| 欧美一级淫片丝袜脚交| 久久久久久久久久国产| 黄色成人在线播放| 色婷婷av一区二区三区久久| 日韩高清电影免费观看完整| 亚洲free性xxxx护士hd| 97久久伊人激情网| 久久精品影视伊人网| 红桃av永久久久| 亚洲夜晚福利在线观看| 亚洲国产精品悠悠久久琪琪| 亚洲欧美三级伦理| 成人亲热视频网站| 欧美激情日韩图片| 成人在线国产精品| 欧美激情va永久在线播放| xxxxx成人.com| 成人性生交大片免费看小说| 中文字幕在线看视频国产欧美| 欧美贵妇videos办公室| 伦伦影院午夜日韩欧美限制| 亚洲综合中文字幕在线观看| 精品视频中文字幕| 亚洲激情自拍图| zzijzzij亚洲日本成熟少妇| 国模叶桐国产精品一区| 亚洲人成绝费网站色www| 欧美激情一区二区三区久久久| 亚洲国产欧美久久| 在线观看国产欧美| 成人信息集中地欧美| 久久99国产精品久久久久久久久| 久久国产精品99国产精| 伦伦影院午夜日韩欧美限制| 成人在线精品视频| 在线精品高清中文字幕| 久久久亚洲欧洲日产国码aⅴ| 成人美女免费网站视频| 国产精品日韩欧美| 欧美在线视频观看| 国内久久久精品| 日韩在线观看免费网站| 久久久久久高潮国产精品视| 亚洲精品98久久久久久中文字幕| 伊人亚洲福利一区二区三区| 成人精品网站在线观看| 精品久久中文字幕久久av| 在线观看日韩视频| 日日狠狠久久偷偷四色综合免费| 国产一区二区三区网站| 亚洲精品国精品久久99热一| 国产精品久久久久久网站| 中国日韩欧美久久久久久久久| 国产精品伦子伦免费视频| 欧美大尺度在线观看| 国产在线不卡精品| 欧美怡春院一区二区三区| 一本大道香蕉久在线播放29| 国产z一区二区三区| 77777亚洲午夜久久多人| 国产成一区二区| 亚洲18私人小影院| 国产精品亚洲视频在线观看| 丝袜情趣国产精品| 国产精品免费久久久| 亚洲国产成人av在线| 亚洲美女性视频| 97视频人免费观看| 国产成人一区二区| 中文字幕少妇一区二区三区| 久久亚洲综合国产精品99麻豆精品福利| 日韩视频免费看| 色一区av在线| 亚洲精品色婷婷福利天堂| 日韩欧美第一页| 精品一区二区电影| 国产精品入口免费视频一| 亚州av一区二区| 4438全国亚洲精品在线观看视频| 欧美激情亚洲国产| 精品国产乱码久久久久久婷婷| 欧美高清videos高潮hd| 91av网站在线播放| 中文字幕免费精品一区高清| 九九热精品视频国产| 97超级碰在线看视频免费在线看| 精品视频在线观看日韩| 92裸体在线视频网站| 亚洲www视频| 欧美在线视频网站| 欧美在线影院在线视频| www.国产精品一二区| 国产精品草莓在线免费观看| 欧洲s码亚洲m码精品一区| 亚洲2020天天堂在线观看| 欧美成人黑人xx视频免费观看| 国产一区二区三区在线观看视频| 亚洲精品suv精品一区二区| 欧美大胆在线视频| 久久精品99久久香蕉国产色戒| 日韩视频在线观看免费| 国内精久久久久久久久久人| 国产亚洲aⅴaaaaaa毛片| 色婷婷亚洲mv天堂mv在影片| 久久久久久久久久久av| 日韩欧美a级成人黄色| 国产欧美精品日韩| 国产精品露脸av在线| 欧日韩在线观看| 亚洲乱亚洲乱妇无码| 最近2019好看的中文字幕免费| 精品国产31久久久久久| 欧美在线视频网站| 日韩精品免费观看| 国产精品99久久久久久久久久久久| 亚洲精品成人久久| 亚洲福利视频久久| 欧洲成人在线视频| 久久久中精品2020中文| 国产日韩换脸av一区在线观看| 亚洲免费av电影| 在线播放日韩专区| 日韩精品极品毛片系列视频| 色婷婷综合成人av| 精品亚洲夜色av98在线观看| 欧美天天综合色影久久精品| 欧洲s码亚洲m码精品一区| 日韩中文字幕网站| 91精品国产综合久久香蕉922| 亚洲一区二区三区xxx视频| 亚洲国产成人精品女人久久久| 国产精品黄色av| 色综合五月天导航| 一级做a爰片久久毛片美女图片| 国产精品观看在线亚洲人成网| 91中文字幕一区| 欧美性精品220|