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

相关推荐
jack_xu21 分钟前
高频面试题:如何保证数据库和es数据一致性
后端·mysql·elasticsearch
pwzs32 分钟前
Java 中 String 转 Integer 的方法与底层原理详解
java·后端·基础
Asthenia041240 分钟前
InnoDB文件存储结构与Socket技术(从Linux的FD到Java的API)
后端
Asthenia04121 小时前
RocketMQ 消息不丢失与持久化机制详解-生产者与Broker之间的详解
后端
〆、风神1 小时前
Spring Boot 整合 Lock4j + Redisson 实现分布式锁实战
spring boot·分布式·后端
Asthenia04122 小时前
Select、Poll、Epoll 详细分析与面试深度剖析/C代码详解
后端
烛阴2 小时前
Node.js中必备的中间件大全:提升性能、安全与开发效率的秘密武器
javascript·后端·express
南雨北斗2 小时前
WMware虚拟机下载方法(2025年4月)
后端
朝阳5812 小时前
Rust项目GPG签名配置指南
开发语言·后端·rust