简介
CgLib(Code Generation Library)是一个针对java,且开源的代码生成类库,封装了ASM,ASM可以直接产生二进制class文件。CgLib能够在运行时动态地生成类的子类,常用于实现动态代理。
CGLib被广泛使用在基于代理的AOP框架(例如SpringAOP和dynaop)
hibernate使用CGLib对持久化对象创建代理。
本篇博客将从三个方面讲述:
- cglib动态代理有什么优点
- 如何生成代理类
- 代理类内容解析
一、cglib动态代理有什么特点
特点 | 动态代理 | 静态代理 |
代理对象创建时间 | 运行时动态生成代理类 | 编码时编写代理类 |
代码维护 | 代理逻辑集中在一个通用的类中,易于维护和修改 | 代理逻辑分散在多个具体代理类中,增加了代码维护的复杂性 |
CgLib动态代理:
优点:
1.被代理的类不必须要实现一个接口,因为通过继承的方式重写被代理类方法
2.FastClass机制,不使用反射调用被代理方法,代码执行速度快
缺点:
1.无法重写的方法不能被cglib代理
2.生成代理类花费时间比较多,因为还要额外生成两个代理类(FastClass相关类),所以单例模式更适合cglib
二、Cglib如何生成代理类
这就要涉及到源码了,Cglib提供生成的代理类的方式是调用Enhancer类的create方法,在创建代理类之前还有两个必要条件:
- superclass:被代理的类
- callback:设置回调的拦截器
//生成一个增强器 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserService.class); //设置要代理的类 enhancer.setCallback(new MyApiInterceptor()); //设置回调的拦截器数组 // 创建代理对象 Person person=enhancer.create(); person.test();
拦截器:
class MyApiInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("被代理方法执行前"); // 此处可以做一些操作 Object result = proxy.invokeSuper(obj, args);//执行被代理方法 System.out.println("被代理方法执行后" ); // 方法调用之后也可以进行一些操作 return result; } }
被代理类:
class UserService { //方法必须能被重写,不能被final修饰 public void test() { System.out.println("调用test方法了..."); } }
下面看Enhancer类的create方法做了什么:
1.调用Enhancer的createHelper()方法:
2.调用super.create(),也就是AbstractClassGenerator的create()方法:
主要看下面的截图:data.get方法生成代理类、return那句返回了代理类对象。
生成代理类的具体代码:
data.get是一个入口,主要看Enhancer的generate()方法
以及Enhancer的generateClass()方法
idea上写的java代码都是.java格式的,经过jvm的编译器编译会生成字节码文件(.class),Cglib使用asm直接生成字节码文件。字节码是由一条条的指令构成,专门学习过才看得懂,所以如何生成代理类不用过分关注,能看懂生成的代理类有哪些内容即可。
生成代理对象的具体代码:
Enhancer的createUsingReflection()方法
三、代理类内容解析
持久化代理类:
Cglib生成的代理类放在内存中,通过dglib提供的调试命令可以将代理类持久化到硬盘。
-Dcglib.debugLocation=E:\project\common\CglibDemo\target\classes\ ##可以持久化到任意目录
重新启动程序执行代理逻辑,会发现指定路径生成了代理类:
代理类内容:
1.继承了Userservice类(被代理类)
2.重写了Userservice(被代理类)的test方法
3.重写了Object类能被重写的方法
Cglib动态代理和jdk动态代理
CGLIB(Code Generation Library)和 JDK(Java Development Kit)中的动态代理是两种不同的实现方式,它们在实现原理和使用方式上存在一些区别。
原理:
JDK动态代理是基于接口的代理。它使用Java的反射机制,在运行时创建一个实现指定接口的代理类。代理类实现了InvocationHandler接口,并将方法调用转发给InvocationHandler处理。
CGLIB动态代理是基于继承的代理。它通过生成目标类的子类来实现代理。代理类继承了目标类,并重写了方法,从而实现了方法拦截和增强的功能。
使用方式:
JDK动态代理要求目标类实现一个接口,代理对象实现了与目标类相同的接口。通过Proxy类的静态方法创建代理对象,将目标对象和InvocationHandler实例传入。
CGLIB动态代理不要求目标类实现接口。通过Enhancer类创建代理对象,将目标对象设置为父类,同时设置MethodInterceptor实例作为拦截器。
性能:
JDK动态代理在调用代理方法时,通过反射调用目标方法,因此会引入一定的性能开销。但对于接口代理,JDK动态代理是唯一的选择。
CGLIB动态代理直接调用目标方法,省去了反射的开销,因此在性能上通常比JDK动态代理略快。但是对于final类、private方法和static方法等无法代理的情况,CGLIB无法生成代理类。
总结
本篇文章带你了解cglib动态代理的外表,它的内部逻辑是如何实现的呢,还遗留了两个问题,博主会为cglib动态代理做一个系列的博客。
- 问题1:代理类执行方法是如何被拦截的
- 问题2:FastClass机制如何实现