一、介绍
CGLIB是强大的、高性能的代码生成库,被广泛应用于AOP框架,它底层使用ASM来操作字节码生成新的类,为对象引入间接级别,以控制对象的访问。CGLIB相比于JDK动态代理更加强大,JDK动态代理只能对接口进行代理,而CGLIB既可以代理普通类,也能够代理接口。
优点:
- 通过FastClass机制调用方法,比JDK动态代理的反射机制效率高;被代理类无需实现接口
缺点:
- 运行期生成字节码,通过ASM写Class字节码,效率低;不能对final类及final方法进行代理
二、工作原理
CGLIB 通过动态生成一个需要被代理类的子类(即被代理类作为父类),该子类重写被代理类的所有不是 final 修饰的方法,并在子类中采用方法拦截的技术拦截父类所有的方法调用,进而织入横切逻辑。此外,因为 CGLIB 采用整型变量建立了方法索引,这比使用 JDK 动态代理更快(使用 Java 反射技术创建代理类的实例)。
2.1步骤说明-生成代理对象
- 创建要被代理的类或接口–MyFly
public class MyFly implements Fly {
@Override
public void doFly() {
System.out.println("wo的");
}
}
- 实现MethodInterceptor并实现intercept方法 --CglibProxy
class CglibProxy implements MethodInterceptor {
/**
* @param o: 代理对象
* @param method: 被代理方法
* @param params: 方法入参
* @param methodProxy: CGLIB方法
**/
@Override
public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
System.out.println("【增强方法】代理对象正在执行的方法:" + method.getName());
Object result = methodProxy.invokeSuper(o, params);
return result;
}
}
- 创建Enhancer(设置要被代理的类和调用方法时触发的拦截器)
public static Object creatCglibProxyObj(Class<?> clazz) {
Enhancer enhancer = new Enhancer();
// 为加强器指定要代理的业务类(即为下面生成的代理类指定父类)
enhancer.setSuperclass(clazz);
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法
enhancer.setCallback(new CglibProxy());
return enhancer.create();
}
- 执行程序
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
Fly fly = (Fly) CglibProxyFactory.creatCglibProxyObj(MyFly.class);
fly.doFly();
}
结果:
2.2 代理对象分析
通过设置属性来输出生成的代理对象
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
通过IDEA打开class文件
2.2.1表头分析
结论:
- 生成的代理对象继承了我们需要被代理的对象同时实现了Factory
- 这里是通过继承的方式来代理的,所以他即可以代理类也可以代理接口
- 因为是继承了被代理类,所以在java中子类是无法重写父类final方法的,这也就解释了为什么CGLIB无法代理final修饰的方法了
2.2.2 stataic 静态代码块
结论:
- 这里面创建了一些后续要试用的变量引用
2.2.3 重写父类方法
结论:
- 重写的方法被final,防止后面被修改
- 这里的var10000就是我们在创建代理时传入的CglibProxy
- 这里也就解释了CGLIB是如何动态增强方法的基本逻辑(在每次调用方法时都会先去调用MethodInterceptor的实现类中的intercept方法,然后我们只需要在intercept方法中实现要加强的代码即可)
2.3 代理方法调用过程分析
2.2.3 调用代理方法
结论:
- 创建代理对象fly
- 通过调用代理对象fly.doFly()方法
3. 调用MethodInterceptor的intercept方法,这里就是调用我们的CglibProxy
4. 调用methodProxy.invokeSuper(o, params);这里就是要调用被代理类的原始方法
- 通过init()来初始化生成代理类和被代理类的FastClass
helper:
- 生成的FastClass文件
- 生成的FastClass文件的invoke方法
- init完成后这里会继续调用fci.f2.invoke(fci.i2, obj, args);
- 这里的fci.f2就是刚刚生成的代理对象FastCalss对象
- 这里程序会传入17,调用代理类的CGLIB$doFly$0()方法;(大家可以debug看,每次生成的文件位置会不一样)
- 调用代理类的CGLIB$doFly$0()
- 到这里基本可以知道它是如何帮助我们调用被代理类的方法了
- 这里的super指的就是MyFly,因为CGLIB生成的代理类继承了我们要被代理的类