CGLIB入门系列三,CGLIB生成的代理类详解

此前一直有一个疑惑,那就是为什么CGLIB生成代理类的时候会出现三个class文件,按道理说应该只有一个。

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;
    }
}

代理类分析

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

public class GoodsService$$EnhancerByCGLIB$$b08e58d5 extends GoodsService implements Factory {
	// 标识拦截器是否已经绑定
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    // 下面的两个变量用来保存回调类,也就是我们设置的拦截器
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    // 这就是我们的拦截器,因为只添加了一个拦截器,所以这里只有一个
    // CGLIB可以添加多个拦截器
    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;
        // 获取了GoodService的父类的所有的方法,这里因为GoodsService只有一个Object父类
        // 如果存在多个父类,这里会有多个加载没有被子类重写的父类的方法
        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
        // 了解一下这里传递的参数,var1现在是Object,var0则是当前生成的代理类
        // 传入了两个方法来计算方法下标
        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");
        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()方法被重写了,所以需要生成当前方法来调用父类的方法
	 */
    final String CGLIB$toString$0() {
        return super.toString();
    }
	
	/**
	 * 生成的代理方法,在该方法中会调用我们设置的Callback
	 * 当调用代理类的toString方法时,先判断是否已经存在实现了MethodInterceptor接口的拦截对象
	 * 如果没有的话就调用CGLIB$BIND_CALLBACKS方法来获取拦截对象
	 */
    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();
    }
	
	/**
	 * 删除了生成的其他代理方法,被代理类中的,每一个方法都会生成上面这种形式的两个方法
	 * 还有几个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中get拦截对象
	 * 如果获取不到的话,再从CGLIB$STATIC_CALLBACKS来获取,如果也没有则认为该方法不需要代理。
     * 那么拦截对象是如何设置到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
	 * 最终实例化代理对象的时候,就在这里将所有的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();
    }
}

FastClass

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

这就是GoodService类生成的对应的FastClass类

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");
    }
}

我们上面代理类中执行的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 CreateInfo(c1, c2);
    return proxy;
}

我们在Callback中执行MethodProxy的invokeSuper方法

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
    	// 调用init方法,就是获取方法下标,且要生成FastClass类
        init();
        FastClassInfo fci = fastClassInfo;
        // f2是被代理类,所以这里是执行被代理类下标为i2的方法
        // 假设我们调用的上面的toString()方法,其实也就是调用的代理类的CGLIB$toString$0方法,这样就实现了原始方法的调用,所以我们才需要执行invokeSuper
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }
}
/**
 * 在相同类型的不同对象上调用原始方法
 * 什么意思呢?CGLIB也可以针对接口实现动态代理
 * 接口实现的动态代理就需要传入一个被代理的实例,然后利用该方法来执行被代理实例的方法
 * 而不是使用上面的invokeSuper
 * 综合所有的代码我们可以看出:
 * invokeSuper是执行生成的代理类的方法,因为f2这个FastClass是代理类的FastClass
 * invoke执行的是原始类的方法,因为f1
 */
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;
}
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);
                fci.i1 = fci.f1.getIndex(sig1);
                fci.i2 = fci.f2.getIndex(sig2);
                fastClassInfo = fci;
                createInfo = null;
            }
        }
    }
}

发布了162 篇原创文章 · 获赞 44 · 访问量 8830

猜你喜欢

转载自blog.csdn.net/P19777/article/details/103998918