cglib使用

导入cglib依赖

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib-nodep</artifactId>
  <version>3.3.0</version>
</dependency>

创建目标类

public class Target {
    public void printName() {
        System.out.println("nemo");
    }

    public void printAge() {
        System.out.println(22);
    }
}

创建拦截器

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before interceptor");

        Object obj = methodProxy.invokeSuper(o, objects);

        System.out.println("after interceptor");
        return obj;
    }
}

测试

public static void main(String[] args) {
    // 设置打印字节码文件
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");
    
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Target.class);
    enhancer.setCallback(new MyMethodInterceptor());
    Target target = (Target) enhancer.create();

    target.printName();
    System.out.println("- - - - -");
    target.printAge();
}

分析目标方法执行过程

打印字节码文件

设置属性打印字节码文件:

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib");

打开cglib目录,看到已经生成了字节码文件

cglib介绍_cglib非反射执行

代理类的printName()方法

在测试的main方法中,实际调用了代理类的printName方法

final void CGLIB$printName$0() {
	// 执行父类(目标类)的方法
    super.printName();
}

public final void printName() {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
        CGLIB$BIND_CALLBACKS(this);
        var10000 = this.CGLIB$CALLBACK_0;
    }

    if (var10000 != null) {
    	// 执行MethodInterceptor方法
        var10000.intercept(this, CGLIB$printName$0$Method, CGLIB$emptyArgs, CGLIB$printName$0$Proxy);
    } else {
        super.printName();
    }
}

从第15行代码看到,拦截器的4个参数分别是

  • this 代理类对象
  • CGLIB$printName$0$Method Method对象,目标类的printName方法
  • CGLIB$emptyArgs 方法的参数
  • CGLIB$printName$0$Proxy MethodProxy对象

以上三个参数在代理类的静态代码块中初始化

static void CGLIB$STATICHOOK1() {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class var0 = Class.forName("org.example.Target$$EnhancerByCGLIB$$1893d03d");
    Class var1;
    // 初始化Object对应的参数
    Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$equals$2$Method = var10000[0];
    CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
    CGLIB$toString$3$Method = var10000[1];
    CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
    CGLIB$hashCode$4$Method = var10000[2];
    CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
    CGLIB$clone$5$Method = var10000[3];
    CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    // 初始化目标类对应的参数
    var10000 = ReflectUtils.findMethods(new String[]{"printName", "()V", "printAge", "()V"}, (var1 = Class.forName("org.example.Target")).getDeclaredMethods());
    CGLIB$printName$0$Method = var10000[0];
    CGLIB$printName$0$Proxy = MethodProxy.create(var1, var0, "()V", "printName", "CGLIB$printName$0");
    CGLIB$printAge$1$Method = var10000[1];
    CGLIB$printAge$1$Proxy = MethodProxy.create(var1, var0, "()V", "printAge", "CGLIB$printAge$1");
}

MethodProxy的create方法和init方法

静态代码块中,初始化MethodProxy,调用了create方法:

public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    MethodProxy proxy = new MethodProxy();
    proxy.sig1 = new Signature(name1, desc);
    proxy.sig2 = new Signature(name2, desc);
    proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
    return proxy;
}
  • c1 目标类型org.example.Target
  • c2 代理类型org.example.Target$$EnhancerByCGLIB$$1893d03d
  • desc 方法参数类型和返回值类型()V
  • name1 目标方法名printName
  • name2 代理方法名CGLIB$printName$0
  • sig1 目标方法签名
  • sig2 目标方法签名
  • createInfo.c1 目标类型org.example.Target
  • createInfo.c2 代理类型org.example.Target$$EnhancerByCGLIB$$1893d03d

MethodProxy类的invokeSuper方法

在拦截器类中,调用了MethodProxy的invokeSuper方法:

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throw var4.getTargetException();
    }
}
  • obj 代理类对象
  • args 方法入参

首先调用init方法初始化FastClass:

private void init() {
    if (this.fastClassInfo == null) {
        synchronized(this.initLock) {
            if (this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(this.sig1);
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci;
                this.createInfo = null;
            }
        }
    }
}

this.createInfo已经在上面的create方法中初始化。

new MethodProxy.FastClassInfo()简单的构造了fci对象,并未初始化其他资源。

fci.f1是目标类的FastClass对象,类型为Target$$FastClassByCGLIB$$c6b7aaff,是上图打印的字节码文件。

fci.f2是代理类的FastClass对象,类型为Target$$EnhancerByCGLIB$$1893d03d$$FastClassByCGLIB$$73d76a45,也是上图打印的字节码文件。

this.sig1、this.sig2上面已经介绍过,在MethodProxy#create方法中初始化。

fci.i2是22(查看字节码文件的getIndex,在第17行代码赋值)

Target$$EnhancerByCGLIB$$1893d03d$$FastClassByCGLIB$$73d76a45#getIndex
public int getIndex(Signature var1) {
    String var10000 = var1.toString();
    switch(var10000.hashCode()) {
	// 忽略其他代码...
    case 1748634388:
        if (var10000.equals("CGLIB$printName$0()V")) {
            return 22;
        }
        break;
    // 忽略其他代码...
    case 2011844968:
        if (var10000.equals("CGLIB$clone$5()Ljava/lang/Object;")) {
            return 10;
        }
    }
    return -1;
}

再回到invokeSuper方法,fci在init方法中做了初始化。

继续看fci.f2的invoke方法

代理类的FastClass对象的invoke方法

public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
    1893d03d var10000 = (1893d03d)var2;
    int var10001 = var1;

    try {
        switch(var10001) {
        case 0:
            return new Boolean(var10000.equals(var3[0]));
        // 忽略其他代码...
        case 22:
            var10000.CGLIB$printName$0();
            return null;
        }
    } catch (Throwable var4) {
        throw new InvocationTargetException(var4);
    }

    throw new IllegalArgumentException("Cannot find matching method/constructor");
}

var1的值是22,在MethodProxy#invokeSuper方法中赋值。

var2是代理类对象。

var3是方法入参。

在invoke方法的11行,执行了代理类的CGLIB$printName$0方法。最后执行了父类(目标类的)printName方法。

final void CGLIB$printName$0() {
    super.printName();
}

执行流程图

这里为cglib调用流程做一下总结,从main方法开始,中间执行了MethodInterceptor#intercept方法,最后执行了目标类Target:printName方法。

cglib介绍_cglib执行过程_02

源代码地址

大家有兴趣可以从gitee上下载我的demo:cglib练习代码

cglib介绍_cglib_03