动态代理最全详解系列[5]-Cglib动态代理源码分析

上一篇我们对Cglib动态代理进行了代码测试,这一篇文章我们看下Cglib实现动态代理的原理,其实分析动态代理弄懂两件事就行:代理类怎么生成的、代理类方法如何调用的被代理类方法。


1 代理类的生成

首先代理类对象是调用enhancer.create()生成的,其中会调用super.create()

public class Enhancer extends AbstractClassGenerator {
    public Object create(Class[] argumentTypes, Object[] arguments) {
        this.classOnly = false;
        if (argumentTypes != null && arguments != null && argumentTypes.length == arguments.length) {
            this.argumentTypes = argumentTypes;
            this.arguments = arguments;
            return this.createHelper();
        } else {
            throw new IllegalArgumentException("Arguments must be non-null and of equal length");
        }
    }    
    private Object createHelper() {
        this.preValidate();
        Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }
}

进入到父类AbstractClassGeneratorcreate()方法,其中做的事情为:通过cglib的生成策略生成代理类的字节码文件,然后加载到jvm,再通过反射创建代理类对象。

public abstract class AbstractClassGenerator<T> implements ClassGenerator {
    protected Object create(Object key) {
        try {
            ClassLoader loader = this.getClassLoader();
            Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
            AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
            if (data == null) {
                Class var5 = AbstractClassGenerator.class;
                synchronized(AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
                        data = new AbstractClassGenerator.ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }

            this.key = key;
            //1.获取代理类对象
            Object obj = data.get(this, this.getUseCache());
            return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
        }
        ...
    }
    
    public Object get(AbstractClassGenerator gen, boolean useCache) {
        if (!useCache) {
            //2.生成代理类对象
            return gen.generate(this);
        } else {
            Object cachedValue = this.generatedClasses.get(gen);
            return gen.unwrapCachedValue(cachedValue);
        }
    }
    
     protected Class generate(AbstractClassGenerator.ClassLoaderData data) {
        Object save = CURRENT.get();
        CURRENT.set(this);
        try {
            ClassLoader classLoader = data.getClassLoader();
            if (classLoader == null) {
                throw new IllegalStateException("ClassLoader is null while trying to define class " + this.getClassName() + ". It seems that the loader has been expired from a weak reference somehow. Please file an issue at cglib's issue tracker.");
            } else {
                String className;
                synchronized(classLoader) {
                    className = this.generateClassName(data.getUniqueNamePredicate());
                    data.reserveName(className);
                    this.setClassName(className);
                }

                Class gen;
                if (this.attemptLoad) {
                    try {
                        gen = classLoader.loadClass(this.getClassName());
                        Class var25 = gen;
                        return var25;
                    } catch (ClassNotFoundException var20) {
                    }
                }
			//3.生成代理类字节码
                byte[] b = this.strategy.generate(this);
                className = ClassNameReader.getClassName(new ClassReader(b));
                ProtectionDomain protectionDomain = this.getProtectionDomain();
                synchronized(classLoader) {
                    if (protectionDomain == null) {
                        //4.根据字节码生成代理类对象
                        gen = ReflectUtils.defineClass(className, b, classLoader);
                    } else {
                        gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                    }
                }

                Class var8 = gen;
                return var8;
            }
        }...
    }
}

上一篇做代码测试时我们保存了代理类相关的字节码文件,会看到如下3个class文件,其中第2个是代理类class,其余两个是代理类的fastclass和被代理类fastclass,这两个稍后再说。

动态代理最全详解系列[5]-Cglib动态代理源码分析

我们先粗略看一下代理类class中的内容,代理类run()方法中,会调用方法拦截器的intercept()方法,方法拦截器会通过某些手段找到代理类的CGLIB$run$0()方法,最后实现调用父类即被代理类Personrun()方法,这个手段在Jdk中是反射,而在Cglib中是一种新的机制-FastClass机制。

public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
    private static final Method CGLIB$run$0$Method;
    private static final MethodProxy CGLIB$run$0$Proxy;

    static void CGLIB$STATICHOOK1() {
        Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
        Class var1;
        CGLIB$run$0$Method = ReflectUtils.findMethods(new String[]{"run", "()V"}, (var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person")).getDeclaredMethods())[0];
        CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
    }

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

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

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$run$0$Method, CGLIB$emptyArgs, CGLIB$run$0$Proxy);
        } else {
            super.run();
        }
    }
}



2 FastClass机制

由于反射的效率较低,Cglib采用FastClass机制实现被代理类方法的调用,FastClass机制就是给一个类的方法建立索引,通过索引直接调用方法。看下面的例子理解一下:

/**
 * @author codeZhao
 * @date 2021/2/20  19:46
 * @Description
 */
public class FastClassTest {
    public static void main(String[] args) {
        Test test = new Test();
        Test$FastClass test$FastClass = new Test$FastClass();
        int index = test$FastClass.getIndex("g()v");
        test$FastClass.invoke(index, test);
    }
}

class Test {
    public void f() {
        System.out.println("f()");
    }
    public void g() {
        System.out.println("g()");
    }
}

class Test$FastClass {
    public Object invoke(int index, Object o) {
        Test t = (Test) o;
        switch (index) {
            case 1:
                t.f();
                return null;
            case 2:
                t.g();
                return null;
        }
        return null;
    }
    public int getIndex(String signature) {
        switch (signature.hashCode()) {
            case 3078511://"f()v的hashCode"
                return 1;
            case 3108302://"g()v的hashCode"
                return 2;
        }
        return -1;
    }
}

动态调用时,假设要调用某Test对象的g()方法,FastClass机制实现方式是首先通过getIndex()传入方法签名信息,获取方法的索引,然后通过invoke()传入方法索引和类对象,执行索引对应方法。如果是反射来实现的话,大概是下面这样:

Test test = new Test();
Method method = Class.forName("Test").getMethod("g");
method.invoke(test);

FastClass机制和反射相比,构建时比较复杂,但运行效率高于反射。



对比上面的小例子,我们从代理类class文件开始,看下FastClass是如何创建的以及代理类如何调用的被代理类方法run()

1.首先,在代理类的静态代码块中,会通过MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0")创建针对run()方法的MethodProxy对象CGLIB$run$0$Proxy,该对象保存有生成FastClass所需的方法签名信息、代理/被代理类的信息。

public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
    static void CGLIB$STATICHOOK1() {
        //代理类Class对象
        Class var0 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person$$EnhancerByCGLIB$$c7ae5d4e");
        //被代理类Class对象
        Class var1 = Class.forName("com.codezhao.designpattern.proxypattern.cglibproxy.Person");
        //创建针对某个方法的MethodProxy对象,该对象保存有代理/被代理方法的签名信息和代理/被代理类的信息
        CGLIB$run$0$Proxy = MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0");
    }
}

//MethodProxy.create(var1, var0, "()V", "run", "CGLIB$run$0")对应的内容
public class MethodProxy {
    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;
    }
}

2.然后在执行代理类的run()方法时,会进入方法拦截器MethodInterceptorintercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)方法,其中o传入代理类Person$$EnhancerByCGLIB$$c7ae5d4e对象,methodProxy传入run()方法的MethodProxy对象CGLIB$run$0$Proxyintercept()中会执行methodProxy.invokeSuper()

methodProxy.invokeSuper()这个方法比较关键,涉及FastClass生成、被代理类方法调用

其中又涉及一个关键的内部类FastClassInfo

private static class FastClassInfo {
    FastClass f1;  //被代理类Person的FastClass
    FastClass f2;  //代理类Person$$EnhancerByCGLIB$$c7ae5d4e的FastClass
    int i1;  //方法run()在f1中的索引
    int i2;  //方法CGLIB$run$0()在f2中的索引
}

3.invokeSuper()方法中首先进入init()检查FastClassInfo是否存在,没有的话就先生成。fci.f2.invoke(fci.i2, obj, args)的作用就是调用代理类FastClass对象f1的invoke()方法,根据传入的索引fci.i2执行代理类对象对应的方法CGLIB$run$0()CGLIB$run$0()会直接调用被代理类的run()方法。

public class MethodProxy {
    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();
        }
    }
}

//fci.f2.invoke(fci.i2, obj, args)对应的内容
public class Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960 extends FastClass {
	public Object invoke(int var1, Object var2, Object[] var3) {
        c7ae5d4e var10000 = (c7ae5d4e)var2;
        int var10001 = var1;

        switch(var10001) {
        ...
        	case 20:
                var10000.CGLIB$run$0();
                return null;
        ...
    }
}

//CGLIB$run$0()对应内容
public class Person$$EnhancerByCGLIB$$c7ae5d4e extends Person implements Factory {
    final void CGLIB$run$0() {
        super.run();
    }
}

通过2、3步就知道了代理类如何调用的被代理类方法。

4.回过头看一下init()FastClassInfo的生成,其中涉及代理/被代理类的FastClass类、代理/被代理方法的索引的生成。具体流程看下面的注释,还是比较清晰的。

public class MethodProxy {
    private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    //createInfo由之前所讲的代理类中的MethodProxy.create(var1, var0, "()V", "run", //"CGLIB$run$0")生成,包含了代理/被代理方法的名字信息和代理/被代理类的信息
                    MethodProxy.CreateInfo ci = this.createInfo;
                    //新建一个FastClassInfo对象
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    //生成被代理类Person的FastClass,f1
                    fci.f1 = helper(ci, ci.c1);
                    //生成代理类Person$$EnhancerByCGLIB$$c7ae5d4e的FastClass,f2
                    fci.f2 = helper(ci, ci.c2);
                    //根据方法run()的签名信息获取其在f1中的索引
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    //根据方法CGLIB$run$0()的签名信息获取其在f2中的索引
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
    private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
        Generator g = new Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }
}

//代理类FastClass的getIndex(),就是根据方法签名信息的hashCode返回索引
//至于方法CGLIB$run$0()的签名信息为什么叫""CGLIB$run$0()V"",想一下之前的MethodProxy.create(var1, var0, "()V", "run", //"CGLIB$run$0")就知道了
public class Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960 extends FastClass {
    public Person$$EnhancerByCGLIB$$c7ae5d4e$$FastClassByCGLIB$$6be6b960(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        ...
        case 53168513:
            if (var10000.equals("CGLIB$run$0()V")) {
                return 20;
            }
            break;
		...
        return -1;
    }
}



3 总结

1.Cglib动态代理中的代理类没有默认继承的父类,所以Cglib对接口和类都可以实现代理。

2.Jdk和Cglib中代理类对象调用被代理类方法的流程都是:通过代理类调用方法拦截器(Cglib中是MethodInterceptor,Proxy中是InvocationHandler),方法拦截器再调用被代理类方法。不同的是:Jdk中调用被代理类方法是通过反射,而Cglib中调用被代理类方法是通过事先建立好的索引,索引的建立和获取在FastClass中

3.因为反射会降低性能,所以Cglib动态代理运行效率优于Jdk动态代理。



欢迎关注我的公众号【codeZhao】,获取更多技术干货和职业思考。

上一篇:模拟Java动态代理模式:CGLIB动态代理


下一篇:Java设计模式之代理模式 - CGLib 动态代理分析