美文网首页java开发进阶JavaAPIJavavaJ
纯手写实现JDK动态代理

纯手写实现JDK动态代理

作者: 张丰哲 | 来源:发表于2017-06-30 22:15 被阅读1902次

    前言

    在Java领域,动态代理应用非常广泛,特别是流行的Spring/MyBatis等框架。JDK本身是有实现动态代理技术的,不过要求被代理的类必须实现接口,不过cglib对这一不足进行了有效补充。本篇博客将涉及2个话题:第一,JDK动态代理的实现原理,带你探索动态代理的实质面目;第二,自己动手写代码去实现JDK动态代理,去创造世界!

    JDK动态代理

    先写一个例子,感性认识下动态代理~

    业务接口:

    interface

    业务实现类:

    interface impl

    业务处理类:

    Handler

    测试类:

    test

    运行结果:

    result

    在JDK动态代理中涉及如下角色:

    业务接口Interface、业务实现类target、业务处理类Handler、JVM在内存中生成的动态代理类$Proxy0

    动态代理原理图:

    动态代理的真实面目

    说白了,动态代理的过程是这样的:

    第一:Proxy通过传递给它的参数(interfaces/invocationHandler)生成代理类$Proxy0;

    第二:Proxy通过传递给它的参数(ClassLoader)来加载生成的代理类$Proxy0的字节码文件;

    我们来看看上面例子中生成的$Proxy0的模样:

    $Proxy0

    首先,$Proxy是实现了我们的业务接口(Man)的,所以客户端显然可以调用业务接口的方法。

    其次,注意到$Proxy是继承自Proxy,并通过构造方法将业务处理类传入给父类Proxy进行初始化。(实质上,你可以看看源码,在Proxy中存在protected InvocationHandler h;)

    初始化Proxy findObject

    很明显,我们看到了业务接口的方法是如何被调用的:

    最终都是回调业务处理类(具体的Handler)的invoke方法完成调用!

    手写代码实现JDK动态代理

    在上面,我们已经分析了JDK动态代理的整个调用过程,接下来,我们就来手写实现它吧!

    先来看一眼图:

    手写实现JDK动态代理

    自定义InvocationHandler:

    MyInvocationHandler

    实现MyInvocationHandler的业务处理Handler:

    MyHandler

    自定义类加载器MyClassLoader:

    MyClassLoader

    为什么要定义一个自定义的类加载器呢?它的作用是什么呢?

    要知道,我们是想手写JDK动态代理,那么我们将自己在内存中生成动态代理类,那么我们如何加载呢?这时候,就可以利用自定义的类加载器做到!

    上述代码,重写了findClass方法,就是为了在指定路径下加载指定的字节码文件。

    自定义MyProxy:

    MyProxy

    MyProxy的作用就相当于JDK的Proxy。MyProxy做了哪些事情呢?

    第一:需要根据interfaces接口构造出动态代理类需要的方法。(其实就是利用反射获取)

    第二:把动态生成的代理类(即.java文件)进行编译,生成字节码文件(即.class文件),然后利用类加载进行加载

    第三:动态代理类进行加载后,利用反射机制,通过构造方法进行实例化,并在实例化时,初始化业务Hanlder

    看一下MyProxy的其他方法:

    编译方法 getMethodString方法 运行结果

    我们来看一眼生成的$MyProxy0:

    $MyProxy0


    OK,到这里,整个JDK的动态代理的实现原理以及手写实现就结束了,你学到了么?

    GoodBye My Friend~

    相关文章

      网友评论

      • _BK_徐静:$MyProxy0.java:3: 警告: Can't initialize javac processor due to (most likely) a class loader problem: java.lang.NoClassDefFoundError: com/sun/tools/javac/processing/JavacProcessingEnvironment

        可以使用但是一直有这么一个警告
      • 袁小坤:JDK动态代理Test中ProxyGenerator.generateProxy方法为何会报错呢,这个类貌似无法引进来。
        袁小坤:@张丰哲 :stuck_out_tongue: 谢谢
        张丰哲:在sun.misc下:smile:
      • 袁小坤:您好,很感谢您的无私分享。有没有源码或者GitHub地址呢?有些代码图片里没有,并不知道如何实现的:innocent:
        袁小坤:@张丰哲 谢谢大牛
        张丰哲:恩恩,下次我改进下,把代码都提供下载地址哈。本篇博客的核心代码都在图片中啦:smile:
      • a072fa038320:大神 问个问题 你是怎么把 那个动态代理类的 $Proxy0的代码搞出来的? 是猜想的,还是通过什么打印出来的?
        张丰哲:同学你好~$Proxy0的代码生成是借助JDK的类生成byte[]来实现打印的,具体的你可以仔细看一下博文中的图片哈~
      • antony_sun:哦明白了, 这第二个参数实际是代表了被代理的类的方法的参数, 会把这参数传递给$Proxy0

        不知道理解的准不准?
        antony_sun:想把sun的那个ProxyGenerator下来看看,可找不到下载途径。 你可以给个能用的链接吗 谢谢
        张丰哲:恩,是的,:blush:
      • antony_sun:这句代码 method.invoke(target, args);

        我的程序第二个参数必须这样写才不报错, 为什么你的程序写null可以通过?
      • antony_sun:大牛,JDK的那段动态代理我按照你写的编译就报错了, 我下载的是jdk8.
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        doBefore();
        method.invoke(target, null);
        doAfter();
        return null;
        }

      本文标题:纯手写实现JDK动态代理

      本文链接:https://www.haomeiwen.com/subject/aiuvcxtx.html