CGLIB生成的代理类详解

https://blog.csdn.net/P19777/article/details/103998918

此前一直有一个疑惑,那就是为什么[CGLIB](https://so.csdn.net/so/search?q=CGLIB&spm=1001.2101.3001.7020)生成代理类的时候会出现三个class文件,按道理说应该只有一个,多出来的两个类怎么回事?
其实多出来的这两个class类就是为CGLIB中重要的fastClass机制而生成的。

## 实验

```java
public class GoodsService {

    @Test
    public void placeOrder() {
        System.out.println("place order");
        MsgUtil.addMsg("place order");
    }

    public void place() {
        throw new RuntimeException();
    }

    @Override
    public String toString() {
        return "GoodsService{}";
    }
}

@Test
public void testEnhance() {
    // 获取CGLIB生成的字节码
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\Users\\12130\\Desktop\\新建文件夹");
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(GoodsService.class);
    enhancer.setCallback(new TransactionInterceptor());
    GoodsService goodsService = (GoodsService) enhancer.create();
    goodsService.placeOrder();
}

static class TransactionInterceptor implements MethodInterceptor {
    /**
     * 参数1:代理类对象实例
     * 参数2:被代理的原始方法
     * 参数3:方法参数
     * 参数4:fastClass机制相关
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("start interceptor");
        // 为什么要执行的是invokeSuper,而不是直接invoke?
        Object obj = methodProxy.invokeSuper(o, objects);
        System.out.println("end interceptor");
        return obj;
    }
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445
```

## FastClass机制

Jdk动态代理的拦截对象是通过反射的机制来调用被拦截实例方法的,反射的效率比较低,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,调用方法时根据方法的签名来计算索引,通过索引来直接调用相应的方法。
这也就是为什么CGLIB生成的动态代理会包含3个class文件的原因,其中一个是生成的代理类,另外两个类都是FastClass机制需要的。另外两个类都继承了FastClass这个类。其中一个class为生成的代理类中的每个方法建立了索引,另外一个则为我们被代理类的所有方法包含其父类的方法建立了索引。
下面这个就是为被代理类中的方法建立的索引。

```java
public class GoodsService$$FastClassByCGLIB$$58067f53 extends FastClass {
    public GoodsService$$FastClassByCGLIB$$58067f53(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case 1203662734:
            if (var10000.equals("placeOrder()V")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 3;
            }
            break;
        case 1858883854:
            if (var10000.equals("place()V")) {
                return 2;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 0;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 4;
            }
        }

        return -1;
    }
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        GoodsService var10000 = (GoodsService)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return var10000.toString();
            case 1:
                var10000.placeOrder();
                return null;
            case 2:
                var10000.place();
                return null;
            case 3:
                return new Boolean(var10000.equals(var3[0]));
            case 4:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

可以看出,利用上面的invoke方法,只要我们传入对应方法的下标和实例对象,就可以非常快的调用到对应的方法。

## FastClass类是在哪生成的?是如何生效的?

我们先看看CGLIB为我们生成的代理类

## 代理类分析

在示例代码中我们通过设置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY的属性值来获取cglib生成的代理类
下面是生成代理类的字节码

```java
public class GoodsService$$EnhancerByCGLIB$$b08e58d5 extends GoodsService implements Factory {
    // 标识拦截器是否已经绑定
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    // 下面的两个变量用来保存回调类,也就是我们设置所有的拦截器,CGLIB会将回调先设置到这两个变量上,然后再进行绑定
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    // 这就是我们的拦截器,因为只添加了一个CallBack,所以这里只有一个
    // CGLIB可以添加多个CallBack
    private MethodInterceptor CGLIB$CALLBACK_0;
    
    private static Object CGLIB$CALLBACK_FILTER;
    // 下面的所有Method都是被代理类的原始的Method
    private static final Method CGLIB$toString$0$Method;
    // MethodProxy与FastClass机制有关,下面会讲
    private static final MethodProxy CGLIB$toString$0$Proxy;
    // 空参数,一个默认值,当我们的方法没有参数的时候会传递给拦截器
    // 不传递空值,这是一种设置思想
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$placeOrder$1$Method;
    private static final MethodProxy CGLIB$placeOrder$1$Proxy;
    private static final Method CGLIB$place$2$Method;
    private static final MethodProxy CGLIB$place$2$Proxy;
    private static final Method CGLIB$equals$3$Method;
    private static final MethodProxy CGLIB$equals$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;
    
    // 该方法会在静态代码块中被调用,对上面的变量进行初始化
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.cp.v5.service.GoodsService$$EnhancerByCGLIB$$b08e58d5");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$3$Method = var10000[0];
        // 创建MethodProxy,每一个方法都会创建一个对应的MethodProxy
        // 当前代理会会为其所有父类的方法都创建对应的MethodProxy,
        CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
        CGLIB$hashCode$4$Method = var10000[1];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[2];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        // 获取GoodsService这个父类的所有方法
        var10000 = ReflectUtils.findMethods(new String[]{"toString", "()Ljava/lang/String;", "placeOrder", "()V", "place", "()V"}, (var1 = Class.forName("com.cp.v5.service.GoodsService")).getDeclaredMethods());
        CGLIB$toString$0$Method = var10000[0];
        CGLIB$toString$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$0");
        CGLIB$placeOrder$1$Method = var10000[1];
        CGLIB$placeOrder$1$Proxy = MethodProxy.create(var1, var0, "()V", "placeOrder", "CGLIB$placeOrder$1");
        CGLIB$place$2$Method = var10000[2];
        CGLIB$place$2$Proxy = MethodProxy.create(var1, var0, "()V", "place", "CGLIB$place$2");
    }
    
    /**
     * 用来调用父类的原始方法
     * CGLIB生成代理利用的是继承,而不是JDK动态代理的利用接口的形式
     * 这样就有一个区别就出现了,JDK动态代理中必须要有一个被代理类的实例
     * 但是CGLIB实现的动态代理就不需要,因为是继承,所以就包含了被代理类的全部方法
     * 但是我们调用生成的代理类实例的toString()方法时,调用的就是CGLIB代理时候的方法
     * 如果调用被代理类的原始方法呢,就是靠下面的这个方法
     * CGLIB生成的代理类中每个原始方法都会有这两种类型的方法
     */
    final String CGLIB$toString$0() {
        return super.toString();
    }
    
    /**
     * 生成的代理方法,在该方法中会调用我们设置的Callback
     * 当调用代理类的toString方法时,先判断是否已经存在实现了MethodInterceptor接口的拦截对象
     * 如果没有的话就调用CGLIB$BIND_CALLBACKS方法来获取Callback
     */
    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        
        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$0$Method, CGLIB$emptyArgs, CGLIB$toString$0$Proxy) : super.toString();
    }
    
    /**
     * 删除了类似toString这样生成的方法,也都是一一对应的
     * 还有几个newInstance()方法,newInstance()方法来源于Factory接口
     * 从上面可以看到生成的代理类实现了Factory接口
     */

    public GoodsService$$EnhancerByCGLIB$$b08e58d5() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }
    
    /**
     * CGLIB$BIND_CALLBACKS 先从CGLIB$THREAD_CALLBACKS(一个ThreadLocal对象)中getCallback
     * 如果获取不到的话,再从CGLIB$STATIC_CALLBACKS来获取,如果也没有则认为该方法不需要代理。
     * 那么CallBack是如何设置到CGLIB$THREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?
     * 在Jdk动态代理中拦截对象是在实例化代理类时由构造函数传入的
         * 在cglib中我们使用Enhancers生成代理类时。是调用Enhancer的firstInstance方法来生成代理类实例并设置回调。
     * firstInstance的调用轨迹为:
           1.Enhancer:firstInstance
           2.Enhancer:createUsingReflection
           3.Enhancer:setThreadCallbacks
           4.Enhancer:setCallbacksHelper
           5.Target$$EnhancerByCGLIB$$788444a0 : CGLIB$SET_THREAD_CALLBACKS
     * 最终CGLIB实例化代理对象的时候,就在这里将所有的Callback成功设置了
     */
    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        GoodsService$$EnhancerByCGLIB$$b08e58d5 var1 = (GoodsService$$EnhancerByCGLIB$$b08e58d5)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }
            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
```

在生成的代理类字节码中,有一个静态代码块,他执行了重要的方法

```java
static {
    CGLIB$STATICHOOK1();
}

static void CGLIB$STATICHOOK1() {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    /**
     * 为被代理类所有方法生成一个MethodProxy对象,包含其所有父类的方法
     */
    Class var0 = Class.forName("com.cp.v5.service.GoodsService$$EnhancerByCGLIB$$b08e58d5");
    Class var1;
    // 获取Object这个父类的所有方法,会将其所有的方法保存到一个本地变量上,并且会生成一个MethodProxy
    // 这个MethodProxy的对象实例很重要,MethodInterceptor的intercept方法就需要该对象实例,用来进行快速调用
    Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$equals$3$Method = var10000[0];
    CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
    CGLIB$hashCode$4$Method = var10000[1];
    CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
    CGLIB$clone$5$Method = var10000[2];
    CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    // 获取GoodsService这个父类的所有方法
    var10000 = ReflectUtils.findMethods(new String[]{"toString", "()Ljava/lang/String;", "placeOrder", "()V", "place", "()V"}, (var1 = Class.forName("com.cp.v5.service.GoodsService")).getDeclaredMethods());
    CGLIB$toString$0$Method = var10000[0];
    CGLIB$toString$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$0");
    CGLIB$placeOrder$1$Method = var10000[1];
    CGLIB$placeOrder$1$Proxy = MethodProxy.create(var1, var0, "()V", "placeOrder", "CGLIB$placeOrder$1");
    CGLIB$place$2$Method = var10000[2];
    CGLIB$place$2$Proxy = MethodProxy.create(var1, var0, "()V", "place", "CGLIB$place$2");
}    
123456789101112131415161718192021222324252627282930
```

`CGLIB$STATICHOOK1()`这个静态方法内执行了MethodProxy.create()方法,为被代理所有的方法都生成了对应的MethodProxy,在调用代理对象的每个方法是都会有一个对应的MethodProxy就会当参数被传递到`MethodInterceptor.intercept()`方法中,在intercept中就能利用MethodProxy类的方法,使用FastClass机制

MethodProxy中的部分方法

```java
/**
 * 一个MethodProxy对象,包含了两个方法的名称,一个是被代理类中的原始方法
 * 另外一个则是代理类中生成的用来调用被代理类原始方法的方法名
 * 当执行MethodProxy.invoke()方法时,其实就是用的被代理类的方法索引
 * 当执行MethodProxy.invokeSuper()方法时,用的是代理类中的方法的索引
 * 
 * c1是代理类实现的父类的class
 * c2是代理类的class
 * name1是被代理类中的原始方法名
 * name2是代理类中新增的用来调用被代理类原始方法的方法的名字
 */
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    MethodProxy proxy = new MethodProxy();
    // Signature是用来标识一个唯一的方法
    // 该标识可以用来获取fastClass中每一个方法的索引
    proxy.sig1 = new Signature(name1, desc);
    proxy.sig2 = new Signature(name2, desc);
    proxy.createInfo = new CreateInfo(c1, c2);
    return proxy;
}

private void init()
{
    /* 
     * Using a volatile invariant allows us to initialize the FastClass and
     * method index pairs atomically.
     * 
     * Double-checked locking is safe with volatile in Java 5.  Before 1.5 this 
     * code could allow fastClassInfo to be instantiated more than once, which
     * appears to be benign.
     */
    if (fastClassInfo == null)
    {
        synchronized (initLock)
        {
            if (fastClassInfo == null)
            {
                CreateInfo ci = createInfo;
                FastClassInfo fci = new FastClassInfo();
                // helper中生成了对应Class的FastClass实例,这里可以看出就是生成了两个Class的FastClass
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                // 获取方法的下标,sig1原本的方法名,sig2是为了调用父类方法生成的特殊方法的方法名
                fci.i1 = fci.f1.getIndex(sig1);
                fci.i2 = fci.f2.getIndex(sig2);
                // 存储在本地变量上
                fastClassInfo = fci;
                createInfo = null;
            }
        }
    }
}

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        // 调用init方法,当前MethodProxy所代表的方法在在FastClass中的索引
        init();
        FastClassInfo fci = fastClassInfo;
        // 这里的f2就是一个FastClass,fci.i2就是在init方法中计算出来的索引
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}

public Object invoke(Object obj, Object[] args) throws Throwable {
    try {
        init();
        FastClassInfo fci = fastClassInfo;
        return fci.f1.invoke(fci.i1, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    } catch (IllegalArgumentException e) {
        if (fastClassInfo.i1 < 0)
            throw new IllegalArgumentException("Protected method: " + sig1);
        throw e;
    }
}

private static class FastClassInfo
{
    // 被代理类的FastClass
    FastClass f1;
    // 代理类的FastClass
    FastClass f2;
    int i1;
    int i2;
}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
```

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值