1. 什么是动态代理?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问,而不是增强目标对象的功能。

访问控制包括同步,身份验证,远程访问(RPC),惰性实例化(休眠,Mybatis),AOP(事务)。

代理方法调用

实现代理的技术有很多,如 CGLIB(ASM)、AspectJ、Javassist、JDK Proxy等。

  • ASM: 针对运行时动态生成和转换类(class)的Java语言工具,旨在处理已编译的Java类(class)。CGLIB浅析
  • AspectJ: AspectJ采用编译时织入和类加载时织入的方式织入切面,是语言级的AOP实现,提供了完备的AOP支持。它用AspectJ语言定义切面,在编译期或类加载期将切面织入到Java类中。
    AspectJ提供了两种切面织入方式,第一种通过特殊编译器,在编译期,将AspectJ语言编写的切面类织入到Java类中,可以通过一个Ant或Maven任务来完成这个操作;第二种方式是类加载期织入,也简称为LTW(Load Time Weaving)。AspectJ
  • Javassist: Javassist在Java中是一个用来编辑字节码的扩展包,它允许Java程序在运行时定义一个新类或者在JVM加载class文件时修改class文件。
    Javassist
  • JDK Proxy: JDK动态代理类是在运行时实现指定的接口列表的类,该类实例上的其中一个接口进行的方法调用时,将被通过统一的接口进行编码并调用到另一个对象。因此,动态代理类可用于为接口列表创建类型安全的代理对象,而无需诸如在编译时使用编译工具预生成代理类。动态代理类实例的代理方法调用将在该代理类实例中的处理程序中通过java.lang.reflect.Method对象进行调用,该对象标识了所调用的方法和包含参数的Object类型数组,翻译自Dynamic Proxy Classes。详情《JDK 动态代理浅析》

在Java语言中,从构建代理类的时期上来看,有三种:编译时、类加载时和运行时。从方式上来说有两种,一种是静态代理,一种是动态代理。
动态代理则是一种方便运行时动态构建代理、动态处理代理方法调用的机制。例如ASM,Javassist,Java Proxy。

编译时类加载时构建代理类则属于静态代理。例如AspectJ。

2. 常用代理方式

由于篇幅问题,本文进行了拆分,详细了解常用的代理方式的原理,请点击下方链接。

JDK proxy

CGLIB浅析

AspectJ

Javassist

3. 应用

jclasslib
图片引用自《美团技术团队》-字节码增强技术探索

FAQ

1.Java生成动态代理的时候,使用WeakCache缓存已经生成的动态代理工厂,疑问点在于,为什么缓存的key使用的是弱引用

答: 类中的静态变量,当它持有一个指向一个对象的引用时,它就作为GC Root,第一类被列为GC Root的元素就是静态成员变量。因此若缓存不再需要时,使用强引用会让GC进行标记分析时认为从GC Root可达,不太会去标记这块内存,反之能够有效地标记这些缓存,从而提高内存回收效率。引用自为什么jdk动态代理类的缓存是弱引用

个人分析: 既然缓存代理工厂类,为什么不使用SoftReference,这样只有内存空间不够时才会进行回收。这样可以最大限度的缓存生成的代理工厂?
如果使用SoftReference,当服务使用动态代理较多时,可能会导致频繁的FullGC。

参考资料

Java动态代理机制分析及扩展,第1部分
Java 动态代理详解
字节码增强技术探索
为什么jdk动态代理类的缓存是弱引用
Java Reflection API
AspectJ LTW(Load Time Weaving)
The AspectJTM Programming Guide
Dynamic Proxy Classes