一切福田,不離方寸,從心而覓,感無不通。

Java动态代理之一CGLIB详解

在上篇文章《Java代理模式及动态代理详解》中我们介绍了Java中的静态代理模式与动态代理模式,并以JDK原生动态代理作为示例进行讲解。本篇文章我们来介绍一下基于CGLIB实现的动态代理,并与原生动态代理进行对比。

CGLIB介绍

CGLIB(Code Generation Library)是一个开源、高性能、高质量的Code生成类库(代码生成包)。

它可以在运行期扩展Java类与实现Java接口。Hibernate用它实现PO(Persistent Object 持久化对象)字节码的动态生成,Spring AOP用它提供方法的interception(拦截)。

CGLIB的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。但不鼓励大家直接使用ASM框架,因为对底层技术要求比较高。

使用实例

首先,引入CGLIB的依赖:

这里我们以操作用户数据的UserDao为例,通过动态代理来对其功能进行增强(执行前后添加日志)。UserDao定义如下:

创建一个拦截器,实现接口net.sf.cglib.proxy.MethodInterceptor,用于方法的拦截回调。

实现MethodInterceptor接口的intercept方法。该方法中参数:

  • obj:表示要进行增强的对象;
  • method:表示要被拦截的方法;
  • objects:表示要被拦截方法的参数;
  • methodProxy:表示要触发父类的方法对象。

在方法的内部主要调用的methodProxy.invokeSuper,执行的原始类的方法。如果调用invoke方法否会出现死循环。

客户端使用示例如下:

执行客户端的main方法打印结果如下:

可以看到,我们方法前后已经被添加上对应的“增强处理”。

反编译class

在main方法内的第一行我们也可以添加如下设置,来存储代理类的class文件。

再次执行程序,我们可以看到在对应目录下生成三个class文件:

部分反编译代码如下:

从反编译的源码可以看出,代理对象继承UserDao,拦截器调用intercept()方法,intercept()方法由自定义的LogInterceptor实现,所以最后调用LogInterceptor中的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

CGLIB创建动态代理类过程

(1)查找目标类上的所有非final的public类型的方法定义;

(2)将符合条件的方法定义转换成字节码;

(3)将组成的字节码转换成相应的代理的class对象;

(4)实现MethodInterceptor接口,用来处理对代理类上所有方法的请求。

JDK动态代理与CGLIB对比

JDK动态代理:基于Java反射机制实现,必须要实现了接口的业务类才生成代理对象。

CGLIB动态代理:基于ASM机制实现,通过生成业务类的子类作为代理类。

JDK Proxy的优势:

最小化依赖关系、代码实现简单、简化开发和维护、JDK原生支持,比CGLIB更加可靠,随JDK版本平滑升级。而字节码类库通常需要进行更新以保证在新版Java上能够使用。

基于CGLIB的优势:

无需实现接口,达到代理类无侵入,只操作关心的类,而不必为其他相关类增加工作量。高性能。

小结

关于动态代理相关的实现就讲这么多,在具体的业务场景中如何选择可根据它们的优缺点进行针对性的比对。不过作为Java领域的标杆项目Spring,很多功能都选择使用CGLIB来实现。

from:https://zhuanlan.zhihu.com/p/115744594