ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-02-25 21:36:34  阅读:143  来源: 互联网

标签:run 代理 Class 源码 CGLIB FastClass 动态 public


上一篇我们对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,这两个稍后再说。

我们先粗略看一下代理类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】,获取更多技术干货和职业思考。

标签:run,代理,Class,源码,CGLIB,FastClass,动态,public
来源: https://www.cnblogs.com/codezhaoblog/p/14449319.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有