彻底搞懂CGLIB代理

如果想了解JDK动态代理可以看我这篇文章:彻底搞懂JDK动态代理

CGLIB(Code Generation Library)是一个强大的高性能的代码生成库,它扩展了Java的字节码操作框架ASM,提供了更加方便的API。CGLIB主要用于动态生成类和代理类,常用于实现AOP(面向切面编程)编程。

CGLIB代理具体实现

CGLIB是针对类实现代理,主要是对指定的类生成一个子类(继承),覆盖其中的方法。

1.定义CGLIB代理的目标类

java 复制代码
//目标类
public class TargetObject {

    public void targetMethod() {
        System.out.println("/// "+this.getClass().getName()+" 执行");
    }
}

2.实现MethodInterceptor接口,重写intercept方法

MethodInterceptor 是一个接口,它定义了方法拦截器应该实现的方法。

java 复制代码
public class AopCglibInvocation implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("/// "+this.getClass().getName()+" 执行前");

        methodProxy.invokeSuper(o, objects);

        System.out.println("/// "+this.getClass().getName()+" 执行后");

        return null;
    }
}

MethodInterceptor接口只有一个方法,即intercept()方法,它负责处理对代理对象的方法调用。

对比JDK动态代理实现的InvocationHandler接口,看到都实现了对应的接口,重写了里面的方法。 区别在于:不再需要代理类的引用对象了 因为intercept()方法中,含有了methodProxy参数,可以直接直接通过proxy 对象访问被代理对象的方法,如果使用method.invoke(将目标对象作为拦截器的成员变量,但是不建议),会出现栈溢出等问题,后面的内容我们会说到为什么不建议

3.通过Enhancer类创建动态代理对象

Enhancer类是CGLIB中的一个类,既能够代理普通的class,也能够代理接口(JDK中的Proxy只能代理接口)

Enhancer类主要用到的方法:

  • enhancer.setSuperclass用来设置代理类的父类,即需要给哪个类创建代理类

  • enhancer.setCallback传递的是MethodInterceptor接口类型的参数,这个MethodInterceptor接口的intercept方法会拦截代理对象所有的方法调用。

  • enhancer.setCallbacks顾名思义 传递的是MethodInterceptor接口类型的参数数组

  • enhancer.setCallbackFilter设置回调选择器,我们通过CallbackFilter可以实现不同的方法使用不同的回调方法

  • enhancer.create方法获取代理对象

scss 复制代码
public static void main(String[] args) {
    // 创建AopCglibInvocation对象
    AopCglibInvocation aopCglibInvocation = new AopCglibInvocation();

    // 创建Enhancer对象
    Enhancer enhancer = new Enhancer();
    // 设置代理对象的超类为TargetObject类
    enhancer.setSuperclass(TargetObject.class);
    // 设置回调对象为AopCglibInvocation对象
    enhancer.setCallback(aopCglibInvocation);

    // 创建代理对象
    TargetObject proxy = (TargetObject) enhancer.create();
    // 调用代理对象的targetMethod方法
    proxy.targetMethod();
}

CGLIB代理原理

通过enhancer.create() 获取到代理接口的class(TargetObject$$EnhancerByCGLIB$$bdec66d0)

enhancer.create()方法Debug后发现最后还是通过反射生成代理对象(TargetObject$$EnhancerByCGLIB$$bdec66d0

通过Arthas反编译TargetObject$$EnhancerByCGLIB$$bdec66d0的源码,可以看到该类继承了目标接口 (ps:也可以通过下面的代码将生成类写入文件中)

Java 复制代码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglib");

从上面源码我们可以看到子类重写了父类的方法,调用了intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

查看我们target的文件夹,我们发现CGLIB除了生成代理对象类,还生成了两个FastClass类( FastClass f1,f2)(JDK动态代理只会生成一个代理类),代理类我们很容易理解,但这两个FastClass类用什么用呢?

FastClass调用机制

CGLIB采用FastClass机制,对代理类和目标类的方法建立签名hash映射,这样就可以直接调用,避免了反射调用目标类(JDK动态代理只能采用反射调用目标类)

CGLIB中我们使用methodProxy.invokeSuper()方法调用目标方法,通过MethodProxy的类的源码,发现其内部保存着对它们的引用

简单来说,多出来的这两个FastClass的作用是:

FastClass-f1:目标类(被代理对象)的映射,所有方法都转发到目标类

FastClass-f2:代理类的映射,所有方法都转发到代理类

在我们调用methodProxy.invokeSuper()方法时,实际是直接利用 FastClass-f2 去调用的代理类,避免了反射调用。

总结

CGLIB代理目标可以不实现接口

CGLIB是针对类实现代理,生成一个之类,所以该类或方法最好不要声明成final, static方法,private方法,final方法是不能被代理的

相关推荐
用户30750093037931 天前
go Eino使用ADK开发agent
后端
唐叔在学习1 天前
Python自动化指令进阶:UAC提权
后端·python
Assby1 天前
Windows 在 PostgreSQL 上安装 vector 扩展
后端
12344521 天前
【面试复盘】有了equals为什么还要hashcode
java·后端
小周在成长1 天前
MyBatis 分页插件PageHelper
后端
Paladin_z1 天前
Easy Query中间件的使用
后端
牛奔1 天前
Go语言中结构体转Map优雅实现
开发语言·后端·macos·golang·xcode
掘金码甲哥1 天前
我不允许谁还分不清这三种watch机制的区别
后端
张心独酌1 天前
Rust新手练习案例库- rust-learning-example
开发语言·后端·rust
码事漫谈1 天前
一文读懂“本体论”这个时髦词
后端