1. 动态代理介绍

动态代理是一项重要的技术。在以下场景中被广泛应用:

  1. 数据库连接
  2. 事务管理
  3. 单元测试中动态mock对象
  4. AOP编程中动态织入横切逻辑
  5. 等等

常见的动态代理实现方式主要是使用JDK和cglib

1.1 jdk

JDK动态代理主要涉及到java.lang.reflect包中的两个类:

  1. Proxy: Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
  2. InvocationHandler: InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

1.2 cglib

CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。

其实现的主要手段是依靠字节码编辑技术

1.3 jdk vs cglib

那么JDK和cglib两种动态代理技术如何选择呢?

使用jdk实现动态代理的问题:动态代理对象的性能相比cglib低很多(字节码技术在动态织入横切逻辑时的性能比反射技术高效,毕竟更加接近底层);只能代理接口

使用cglib实现动态代理问题:cglib创建代理对象上的开销比JDK实现的方式大。如果需要频繁创建代理对象,则不太适合使用cglib

1.4 动态代理的问题

无论是cglib还是jdk来实现都会存在一些问题:

  1. 目标类的所有方法都添加了横切逻辑,有时候我们只想对业务中的某些特定方法添加横切逻辑
  2. 我们通过硬编码的方式指定了织入横切逻辑的织入点
  3. 我们手工编写代理实例的创建过程,为不同类创建代理时,需要分别编写相应的程序代码,无法做到通用。

如果要解决这些问题,可以采用spring aop来实现动态代理(其底层实现当然也是通过cglib或者jdk,只不过做了封装,所以我们一般说是两种实现动态代理的方法)。

2. jdk动态代理

假设我们需要添加性能监控的横切逻辑。我们这里的性能监控要求很简单,就是统计程序的运行时间是多少,并且在开始程序和结束程序的时候都打印下时间戳信息。

简单概括下用jdk实现动态代理的步骤:

  1. 编写自己的handler类(要实现InvocationHandler接口)
  2. 创建代理实例

相关代码见我的github:PerformanceHandlerJDK.java

运行效果:

注意: jdk动态代理只能代理接口。这个从newProxyInstance的方法签名就可以看出来。

3. cglib动态代理

cglib除了代理对象性能高之外,还有个重要优点就是可以代理类。gclib可以创建一个子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

使用cglib实现动态代理的基本步骤:

  1. 创建一个gclib代理类
  2. 使用代理类来代理目标类
  3. 调用目标类方法,会自动调用织入的代码

具体代码见:PerformanceCGLibProxy.java

参考资料:

  1. Java动态代理之JDK动态代理和CGLib动态代理 面向切面编程AOP原理
  2. Spring 3.x 企业应用开发实战