• Java JDK 动态代理使用及实现原理分析


    http://blog.jobbole.com/104433/

    一、什么是代理?

    代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    代理模式UML图:

    为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

    二、Java 动态代理类

    Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

    (1)Interface InvocationHandler:该接口中仅定义了一个方法

    [java] view plain copy

    1. publicobject invoke(Object obj,Method method, Object[] args)

    在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

    (2)Proxy:该类即为动态代理类,其中主要包含以下内容:

    protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

    static Class getProxyClass (ClassLoaderloader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

    static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

    所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

    在使用动态代理类时,我们必须实现InvocationHandler接口

    通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

    动态代理步骤
    1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
    2.创建被代理的类以及接口
    3.通过Proxy的静态方法
    newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
    4.通过代理调用方法

    三、JDK的动态代理怎么使用?

    1、需要动态代理的接口:

    2、需要代理的实际对象

    3、调用处理器实现类

    4、测试

    5、输出结果如下:

    演示demo下载地址:http://download.csdn.net/detail/xunzaosiyecao/9597388

    四、动态代理怎么实现的?

    从使用代码中可以看出,关键点在:

    通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。
    也就是说,当代码执行到:
    subject.SayHello(“jiankunking”)这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?
     
    ======横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论======
    以下代码来自:JDK1.8.0_92
    既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?
    我们再进去getProxyClass0方法看一下:

    真相还是没有来到,继续,看一下proxyClassCache
    奥,原来用了一下缓存啊

    那么它对应的get方法啥样呢?


    我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。
    来瞅瞅,get里面又做了什么?

    发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:

    通过看代码终于找到了重点:

    那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

    我们用jd-jui 工具将生成的字节码反编译:

    这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口
    也就是说:
    这里的subject实际是这个类的一个实例,那么我们调用它的:
    就是调用我们定义的InvocationHandlerImpl的 invoke方法:

    ======横线之间的是代码跟分析的过程,不想看的朋友可以直接看结论=======

    五、结论

    到了这里,终于解答了:
    subject.SayHello(“jiankunking”)这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?

    因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。

    包含生成本地class文件的demo:http://download.csdn.net/detail/xunzaosiyecao/9597474

    通过分析代码可以看出Java 动态代理,具体有如下四步骤:

      1. 通过实现 InvocationHandler 接口创建自己的调用处理器;
      2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
      3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
      4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
  • 相关阅读:
    vscode Nodejs 调试 相关总结
    编程语言中的foo,bar到底是什么
    带T和带Z的相关时间是什么 及关于时间的一些知识
    自定义Firefox的 "切换previous标签页"快捷键, 增加"切回last标签页"快捷键
    开始使用Firefox
    Fork-Join 原理深入分析(二)
    Fork-Join分治编程介绍(一)
    Executor框架(七)Future 接口、FutureTask类
    Executor框架(六)CompletionService 接口
    Executor框架(五)Executors工厂类
  • 原文地址:https://www.cnblogs.com/YDDMAX/p/7385084.html
Copyright © 2020-2023  润新知