jdk动态代理源码解析

1.简介

          动态代理在java框架盛行的今天,用的非常广泛,在诸多设计模式中也是比较难掌握的。动态代理实际上是一种字节码增强技术。产生一个新的代理类来辅助原有的类来进行某些增强操作。在spring的aop中使用的核心技术就是动态代理。常用的动态代理技术实现方案大体上有两种,jdk的动态代理和cglib的动态代理。jdk的动态代理使用的是jdk自带的api实现的。和cglib相比,不管是在效率还是局限性上都稍弱一点。本文今天主要讲的是jdk的动态代理,jdk是基于版本1.7的。

jdk动态代理必须要求被代理类要实现接口,如果没有实现接口,会导致代理失败。jdk动态代理生成的代理类通常是以$Proxy.class类似开头的类。类是直接加载到内存中。本文中,会将生成的class类尝试写到class文件中。便于分析动态代理的过程。


2.创建一个动态代理的demo       

创建一个动态代理大致需要四步:

  1)新建业务接口

   动态代理中会通过class.forName()创建对应业务接口的class对象,并且在这个class对象的基础上,生成一个代理类。这也是为什么jdk动态代理要求必须实现接口的原因。而cglib动态代理是根据当前的类生成一个代理类,cglib中只要类不是以final修饰的,都可以生成代理类

jdk动态代理源码解析

  2)新建业务实现类

jdk动态代理源码解析

  3)新建代理类处理类

        jdk生成的代理对象,其实是继承了InvocationHandler,实现了业务接口的,后面分析生成的动态代理类时,会展示。

jdk动态代理源码解析

  4)创建代理类测试类

jdk动态代理源码解析

    类图如下:

jdk动态代理源码解析


3.jdk动态代理的时序图

    jdk动态代理源码解析

4.动态代理源码解析

说明:按照上面时序图的步骤,进行代码演示和说明:

  1)在单元测试方法中,调用测试方法,测试:jdk创建单元测试的步骤,上面已经说过,省略。。。

jdk动态代理源码解析

  2)调用Proxy.newProxyInstance(ClassLoaderloader, Class<?>[] interfaces,InvocationHandler h)创建代理对象,返回的代理对象是新生成的class文件,通常为:$Proxy开头的class文件。

jdk动态代理源码解析

  3)调用Proxy的内部方法,尝试去缓存中加载class对象。

jdk动态代理源码解析

  4)proxyClassCache实际上是一个WeakCache对象,调用WeakCache的get方法

jdk动态代理源码解析

  5)当创建代理对象时,subKeyFactory的实际类型为ProxyClassFactory,调用ProxyClassFactory的apply方法创建代理对象。

jdk动态代理源码解析

  6)根据系统中判断生成的$Proxy代理类的名称和目标对象类的接口,生成代理对象,实际上是一个字节数组,我们稍后回来操作这个字节数组,生成代理类。

jdk动态代理源码解析


  第七步、第八步、第九步。第十步就是返回代理对象到Proxy中。

  11)第二步,调用完成之后,返回了$Proxy的class对象,下一步就是根据这个class对象生成相应的实例,java中生成对象的方式有很多种,我们下面这种就是调用class对象的构造器生成对象。

jdk动态代理源码解析

5.将动态代理类生成到硬盘中。

jdk动态代理源码解析

使用反编译工具打开生成的class文件:

jdk动态代理源码解析


生成的代理类,也实现了被代理类的接口,并且继承了proxy.

时序图11步中,我们说过,被代理类是通过调用Constructor.newInstance(InvocationHandler)生成对象的。

我们来看看调用父类的Proxy(InvocationHandler)是怎么处理的:

jdk动态代理源码解析

实际上是对h进行了赋值,h是什么呢?在哪里会用到呢?接着往下看。我们再使用代理对象调用方法时,实际上调用的是$Proxy的方法,我们再测试中调用的是addUser()方法,看看$Proxy中是怎么实现的:

jdk动态代理源码解析



6.总结和小计

jdk动态代理用到了字节码增强技术,并且将增强后的类新建了一份。返回的代理对象实现了业务接口,继承了Proxy类。通过创建代理对象传递的UserServiceProxy对象,调用其中的invoke()方法,进行了增强。

UserServiceProxy中invoke()中的第一个参数,我们也可以看出它其实是指向了$Proxy,所以,这个类我们可以适当的进行一些处理。