彻底搞懂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方法是不能被代理的

相关推荐
高频因子挖掘机12 分钟前
量化回测应该保存不复权数据吗?从数据口径到收益计算的工程实践
后端·github·api
高频因子挖掘机1 小时前
实时行情为什么不应该逐只股票请求?批量查询与标的池管理实践
后端·github·api
程序猿DD6 小时前
OctaFuse Gateway 2.15.0:MCP 聚合、向量 Embeddings 与官方目录同步
后端·agent
余槐i9 小时前
PostgreSQL 函数索引报错 must be immutable:3种绕法与代价
数据库·后端·sql·postgresql·性能优化
需要8269 小时前
Spring Bean 生命周期:从实例化到销毁的完整钩子
java·后端·spring
凤山老林10 小时前
Spring Boot 接入国密 SM2/SM3/SM4 的一些实践记录
spring boot·后端·python
伯恩bourne11 小时前
Spring Boot Bean 注册的两种方式:@Component 家族 vs @Configuration + @Bean
java·spring boot·后端
张彦峰ZYF12 小时前
停顿短不等于延迟低:G1、ZGC 与低延迟服务的目标分野、代价账本与选型标定
后端·回收暂停、应用停止总时长·到达安全点、分配停滞·屏障吃吞吐、并发线程吃核心·并发窗口吃堆余量·分区式靠挑回收集兑现目标·染色指针式不支持压缩引用
pride.li12 小时前
第03章 Python 教程-基础语法:注释与变量
服务器·前端·后端·python·算法·计算机视觉
upala13 小时前
【仓颉语言入门 · 第二篇 第 6 课】
后端