CGLIB
CGLIB,即Code Generation Library,是一个开源项目。Github地址:https://github.com/cglib/cglib。
CGLIB的github简介:CGLIB - 字节码生成库,是用于生成和转换Java字节码的高级API。它被AOP、测试、数据访问框架用于生成动态代理对象和拦截字段访问。
CGLIB提供两种类型的JAR包:
- cglib-nodep-x.x.x.jar:使用nodep包不需要关联ASM的jar包,jar包内部包含ASM的类库。
- cglib-x.x.x.jar:使用此jar包需要另外提供ASM的jar包,否则运行时报错,建议选用不包含ASM类库的jar包,可以方便控制ASM的。
本文中使用的CGLIB依赖为:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.10</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
CGLIB基本原理
- 基本原理:动态生成一个要代理类的子类(被代理的类作为继承的父类),子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用Java反射的JDK动态代理要快,因为它采用了整形变量建立了方法索引。
- 底层实现:使用字节码处理框架ASM,用于转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求必须对JVM内部结构包括class文件的格式和JVM指令集都很熟悉,否则一旦出现错误将会是JVM崩溃级别的异常。
CGLIB的包结构
- net.sf.cglib.core:底层字节码处理类,大部分与ASM有关。
- net.sf.cglib.transform:编译期或运行期类和类文件的转换。
- net.sf.cglib.proxy:实现创建代理和方法拦截器的类。
- net.sf.cglib.reflect:反射相关工具类。
- net.sf.cglib.util:集合排序等工具类。
- net.sf.cglib.beans:JavaBean相关的工具类。
CGLIB常用API
下面介绍一下CGLIB中常用的几个API,先建立一个模特接口类和普通模特类:
public class SampleClass {
public String sayHello(String name) {
return String.format("%s say hello!", name);
}
}
public interface SampleInterface {
String sayHello(String name);
}
Enhancer
Enhancer,即(字节码)增强器。它是CGLIB库中最常用的一个类,功能与JDK动态代理中引入的Proxy类差不多,但是Enhancer既能够代理普通的Java类,也能够代理接口。
Enhancer创建一个被代理对象的子类并且拦截所有的方法调用
(包括从Object中继承的toString和hashCode方法)。
Enhancer不能够拦截final方法,例如Object.getClass()方法,这是由于final关键字的语义决定的。基于同样的道理,Enhancer也不能对fianl类进行代理操作。这也是Hibernate为什么不能持久化final关键字修饰的类的原因。
public class EnhancerClassDemo {
public static void main(String[] args) throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
//使用FixedValue,拦截返回值,每次返回固定值"Doge say hello!"
enhancer.setCallback((FixedValue) () -> "Doge say hello!");
SampleClass sampleClass = (SampleClass) enhancer.create();
System.out.println(sampleClass.sayHello("throwable-10086"));
System.out.println(sampleClass.sayHello("throwable-doge"));
System.out.println(sampleClass.toString());
System.out.println(sampleClass.getClass());
System.out.println(sampleClass.hashCode());
}
}
输出结果: