cglib 是一个功能强大、高性能、高质量的字节码操作库,主要用于在运行时扩展 Java 类或者根据接口生成对象。
cglib 在一些开源框架中使用很广泛,比如 Spring, 数据库开源库 Hibernate,以及测试框架 mockito。正是因为 cglib 把脏活累活都干了,这些框架使用才很方便。
这是一个开源的库,cglib 本身的实现基于 asm 库。相比于 asm 库,cglib 的接口更友好,使用起来更简单。
下面介绍 cglib 的主要接口和类以及基于 cglib 的动态代理实现。
首先要说到的就是 Enhancer 这个类,这个是 cglib 中使用的最多的类。之前 JDK 中使用反射来实现动态代理时,必须要基于接口来生成动态代理类,而 Enhancer 可以直接基于类来代理类。
Enhancer 可以生成被代理类的子类,并且拦截所有方法的调用,也就是通常所说的增强。
需要注意,Enhancer 不能增强构造函数,也不能增强被 final 修饰的类,或者被 static 和 final 修饰的方法。
如果不想直接生成一个对象,cglib 也可以生成一个 Class 对象,用这个 Class 对象生成对象或者其他操作。
Enhancer 的使用分为两步,传入目标类型,设置回调。支持不同类型回调是 cglib 最强大的地方。
在 Enhancer 创建一个代理类之后所实现的行为要通过这些回调来实现。
常见的的回调类型如下:
- FixedValue:返回一个固定的值
- InvocationHandler:增强方法,添加额外的功能
- MethodInterceptor:与 InvocationHandler 功能类似,但是控制的权限更多
- LazyLoader:可以延迟加载被代理的对象,而且每个对象只会被创建一次
- Dispatcher:与 LazyLoader 功能基本相同,但是在获取对象时,每次都会创建不同的对象
- ProxyRefDispatcher:与 Dispatcher 功能类似,但是会多一个被代理对象的参数
- NoOp:什么也不做
对于 FixedValue 类型的回调,调用所有的方法都会返回这个固定的值,如果类型不匹配,就会报错。
除了 static 和 final 类型的方法,其他所有的方法都会执行上面的代码,打印 。但是需要注意的是,如果某个方法返回的类型和上面的代理行为不一致就会报错,java.lang.Object 中的方法也是一样。
看到这个是不是很熟悉,这个与 JDK 反射自带的 InvocationHandler 基本一致。
只想改变其中部分方法的行为,其他的方法还的行为不变,最简单的思路就是如果不是目标方法就会调用本来的实现,假设要调用 方法:
这个代码并不会正常返回结果,而是进入无限循环,这是因为这个代理对象的每一个可以被代理的方法都被代理了,在调用被代理了的方法时,会重复进入到 这个方法中,然后进入死循环。
解决办法如下。
在 MethodInterceptor 中,有一个 参数,这个就可以用来执行父类方法。
用来延迟加载对象。
在下面这个例子中,使用 LazyLoader 来延迟加载一个 ArrayList 对象。
上面代码的执行结果如下:
在执行 data.get(0) 的时候,ArrayList 才会被创建,也就是说只有在被使用的,才会去创建对象,而且每个对象只会被创建一次。
Dispatcher 与 LazyLoader 的不同之处在于,每次去获取对象的时候都会创建一个新的对象,而不是复用同一个对象。
上面代理的执行结果如下,每次使用对象都会创建一个新的对象。
ProxyRefDispatcher 与 Dispatcher 功能类似,但是多了一个参数,使用这个回调同样要注意无限循环的问题。
这个回调什么也不做,会完全继承被代理类的功能,所以 NoOp 不能使用接口来创建代理,只能使用类。
这个回调在一些特定的情况下还是挺有用的,看下面的例子。
在上面的例子中,都是给 Enhancer 设置了单个回调,其实每个 Enhancer 可以设置多个回调,这里就需要用到 。
比如现在就需要对 方法进行处理,其他的方法保持父类的行为就可以,按照这个要求,实现的 CallbackFilter 如下:
调用结果如下,只有 sayHello 方法会被拦截,其他的方法不会有变动。
上面介绍了 Enhancer 之后,实现动态代理应该就不难了。
InvocationHandler 和 MethodInterceptor 都可以用来实现动态代理,下面是两种实现。
使用上面的实现来创建代理并调用方法:
这里需要注意,如果使用接口创建代理对象,第一行代码使用 或者 创建对象传入都可以,如果使用是父类创建代理对象,那么只能使用第二种。
然后创建代理对象并调用:
通常来说,使用 MethodInceptor 方式来实现更好,因为可以避免出现上面说到的无限循环的问题。
cglib 相比于 Java 反射实现动态代理的优势就是不受类的限制,可以自由的选择根据接口或者类来生成新的代理对象。
在 JDK 中,动态代理主要由 来实现,在 cglib 中,同样也实现了 Proxy,功能于 JDK 中的功能基本一致,其中用到的 就是前面介绍的回调。
这样做是为了 JDK1.3 以前的版本也能使用动态代理的功能。
这篇文章主要介绍了 cglib 的如果通过 Enhancer 去生成代理类,可以同时支持接口和子类的两种方式。同时也介绍了通过 Enhancer 来实现动态代理。
cglib 的能力远不止这些,下篇文章将介绍 cglib 的其他功能。
[1] dzone.com/articles/cg…
文 / Rayjun

版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/11354.html