9、cglib demo分析以及methodProxy与Fastclass源码

前言

上一节讲了say方法最终会转发,在demo中
cglib.CglibProxy#intercept这个里面用了

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

这个invokeSuper是什么?如何实现代理类函数的调用 转发到 父类对应函数的调用.

这里就涉及methodProxy以及FastClass机制了。
这两者也是紧密联系的。

源码分析

方法代理的创建 CGLIB$methodName$index$Proxy

先直接讲结论
*** 方法代理的作用就是指明class1.method1方法对应的代理方法是class2.method2 ***

再看代码
先看cglib.CglibProxy#intercept在前面代理类class反编译文件中传递的参数

Object object = methodInterceptor.intercept((Object)this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);

//CGLIB$say$0$Proxy是什么
CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//其中
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

上面代码中,MethodProxy#create
就是根据参数进行"签名"

  A representation of a method signature, containing the method name,
  return type, and parameter types.

签名包含方法名称,返回类型,以及参数类型,看源码如下

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

*** 这一段的意义就是说,委托类c1的方法name1,对应的代理方法是实现类c2的方法name2***

方法代理的调用

再看net.sf.cglib.proxy.MethodProxy#invokeSuper干了啥

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

//init函数干了什么
    private void init()
    {
        /*
        单例模式实现
         */
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    /*
                    生成fastClass f1,f2,结合class文件分析
                     */
//                    System.out.println("net.sf.cglib.proxy.MethodProxy.helper " + ci.c1.getName() + "  " + ci.c2.getName() + " " + this.sig1 + " " + this.sig2);
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    /*
                    获取当前方法在f1,f2中的签名,得到一个index(方法与签名,index是一一对应的关系)
                    可以根据index映射对应类的对应方法
                     */
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

//helper函数干了什么
        private static FastClass helper(CreateInfo ci, Class type) {
        //根据ci得到该方法的委托类,实现类,分别生成这两个类的fastClass
        FastClass.Generator g = new FastClass.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创建于调用

可以直接参考 http://www.cnblogs.com/cruze/p/3865180.html
这里说的比较简单易懂
主要思想就是在为了避免方法调用时,过度使用反射造成调用慢的问题
给每一个方法一个签名,遇到这个签名时,直接显示调用实现类的实现方法,
可以参考下面的fastClass文件

fastClass的创建

fastClass的创建时机

从demo中看,fastClass是根据这样的顺序创建的

net.sf.cglib.proxy.MethodProxy#invokeSuper
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create

*** 这里有一种lazy init的思想 ***
也就是说,一开始并没有fastClass,只有MethodProxy
但是当 方法代理 真正被请求的时候
class1的method1方法需要映射到class2的method2方法时,
为了避免过度使用反射,才生成了fastClass方便的完成调用
为了方便理解,

fastClass的创建了几次

即生成了几个fastClass类的class文件
*** 两个,f1和f2(理解成是fast(c1)和fast(c2)) ***
net.sf.cglib.proxy.MethodProxy#init里面调用了两次net.sf.cglib.proxy.MethodProxy#helper
这两个文件的区别是什么

对象意义,实例
c1cglib.CglibLearn$serviceImpl
f1c1的fastClass,更方便调用c1的函数 cglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381
c2cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51 即c1的enhance class
f2c2的fastClass,更加方便调用c2的函数, cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902

f1,f2都是对原有的c1,c2进行了method->signature->index的映射

fastClass类的内容是如何generate出来的

net.sf.cglib.reflect.FastClass.Generator#generateClass
这里就不细讲了,和上一节Enhancer#generateClass干的事情一样,难度更小,底层还是利用asm生成。

fastClass类的内容是什么

以f1为例 (f2后面筛选一部分出来),即
cglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381为例子,class反编译的代码如下

package cglib;
import cglib.CglibLearn.serviceImpl;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;

/* compiled from: <generated> */
public class CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381 extends FastClass {
    public CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381(Class cls) {
        super(cls);
    }

    public int getIndex(String str, Class[] clsArr) {
        switch (str.hashCode()) {
            case -1776922004:
                if (str.equals("toString")) {
                    switch (clsArr.length) {
                        case 0:
                            return 5;
                        default:
                            break;
                    }
                }
                break;
            case -1295482945:
                if (str.equals("equals")) {
                    switch (clsArr.length) {
                        case 1:
                            if (clsArr[0].getName().equals("java.lang.Object")) {
                                return 4;
                            }
                            break;
                        default:
                            break;
                    }
                }
                break;
            case -1039689911:
                if (str.equals("notify")) {
                    switch (clsArr.length) {
                        case 0:
                            return 8;
                        default:
                            break;
                    }
                }
                break;
            case 113643:
                if (str.equals("say")) {
                    switch (clsArr.length) {
                        case 0:
                            return 0;
                        default:
                            break;
                    }
                }
                break;
            case 3641717:
                if (str.equals("wait")) {
                    switch (clsArr.length) {
                        case 0:
                            return 1;
                        case 1:
                            if (clsArr[0].getName().equals("long")) {
                                return 3;
                            }
                            break;
                        case 2:
                            if (clsArr[0].getName().equals("long") && clsArr[1].getName().equals("int")) {
                                return 2;
                            }
                        default:
                            break;
                    }
                }
                break;
            case 147696667:
                if (str.equals("hashCode")) {
                    switch (clsArr.length) {
                        case 0:
                            return 6;
                        default:
                            break;
                    }
                }
                break;
            case 1902066072:
                if (str.equals("notifyAll")) {
                    switch (clsArr.length) {
                        case 0:
                            return 9;
                        default:
                            break;
                    }
                }
                break;
            case 1950568386:
                if (str.equals("getClass")) {
                    switch (clsArr.length) {
                        case 0:
                            return 7;
                        default:
                            break;
                    }
                }
                break;
        }
        return -1;
    }

    public int getIndex(Signature signature) {
        String obj = signature.toString();
        switch (obj.hashCode()) {
            case -1725733088:
                if (obj.equals("getClass()Ljava/lang/Class;")) {
                    return 7;
                }
                break;
            case -1026001249:
                if (obj.equals("wait(JI)V")) {
                    return 2;
                }
                break;
            case -909388886:
                if (obj.equals("say()V")) {
                    return 0;
                }
                break;
            case 243996900:
                if (obj.equals("wait(J)V")) {
                    return 3;
                }
                break;
            case 946854621:
                if (obj.equals("notifyAll()V")) {
                    return 9;
                }
                break;
            case 1116248544:
                if (obj.equals("wait()V")) {
                    return 1;
                }
                break;
            case 1826985398:
                if (obj.equals("equals(Ljava/lang/Object;)Z")) {
                    return 4;
                }
                break;
            case 1902039948:
                if (obj.equals("notify()V")) {
                    return 8;
                }
                break;
            case 1913648695:
                if (obj.equals("toString()Ljava/lang/String;")) {
                    return 5;
                }
                break;
            case 1984935277:
                if (obj.equals("hashCode()I")) {
                    return 6;
                }
                break;
        }
        return -1;
    }

    public int getIndex(Class[] clsArr) {
        switch (clsArr.length) {
            case 0:
                return 0;
            default:
                return -1;
        }
    }

    public int getMaxIndex() {
        return 9;
    }

    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
            case 1:
                cglib_CglibLearn_serviceImpl.wait();
                return null;
            case 2:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue(), ((Number) objArr[1]).intValue());
                return null;
            case 3:
                cglib_CglibLearn_serviceImpl.wait(((Number) objArr[0]).longValue());
                return null;
            case 4:
                return new Boolean(cglib_CglibLearn_serviceImpl.equals(objArr[0]));
            case 5:
                return cglib_CglibLearn_serviceImpl.toString();
            case 6:
                return new Integer(cglib_CglibLearn_serviceImpl.hashCode());
            case 7:
                return cglib_CglibLearn_serviceImpl.getClass();
            case 8:
                cglib_CglibLearn_serviceImpl.notify();
                return null;
            case 9:
                cglib_CglibLearn_serviceImpl.notifyAll();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
        invocationTargetException = new InvocationTargetException(th);
    }

    public Object newInstance(int i, Object[] objArr) throws InvocationTargetException {
        switch (i) {
            case 0:
                try {
                    return new serviceImpl();
                } catch (Throwable th) {
                    InvocationTargetException invocationTargetException = new InvocationTargetException(th);
                }
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
}

如何理解上面这段代码,先看父类fastClass的几个函数定义

//根据方法签名的hashCode,获取一个method的标识index
net.sf.cglib.reflect.FastClass#getIndex(net.sf.cglib.core.Signature)

//根据method的标识index,调用实现类的对应方法
net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])

*** 为什么说fastClass比反射快 ***
net.sf.cglib.reflect.FastClass#invoke(int, java.lang.Object, java.lang.Object[])函数中,直接创建了实现类对象,通过标识符index去switch case进行对应调用,就像上面的

    public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }
//省略

结果分析

上面invokeSuper返回

fci.f2.invoke(fci.i2, obj, args)

是干了什么
表示用方法代理(否则是f1,这里是f2),将lazy init里面记录好的方法签名对应的标志i2传递过去,让f2进行对应的处理

上面fastclass是f1的,这里只贴出来f2对应部分,f2比较长

类名
cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51$$FastClassByCGLIB$$8a094902

    public int getIndex(Signature signature) {
        String obj = signature.toString();
        switch (obj.hashCode()) {
            case 1540695073:
                if (obj.equals("CGLIB$say$0()V")) {
                    return 17;
                }
                break;
         }
     }

     public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        b2e6ff51 cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51 = (b2e6ff51) obj;
        switch (i) {
            case 17:
                cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0();
                return null;
            default:
                throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }


//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()函数
    final void CGLIB$say$0() {
        super.say();//即CglibLearn.serviceImpl.say()方法
    }

这里就完成了c1.say到c2.CGLIB$say$0()的转发
可能会有疑问,为什么不是c1.say到c2.say的转发
这是因为在c2里面定义了(上一节也有这段代码,本节最上也有)

CGLIB$say$0$Proxy = MethodProxy.create(class_2, class_, (String)"()V", (String)"say", (String)"CGLIB$say$0");

//其中
Class class_2 = Class.forName("java.lang.Object");
Class class_ = Class.forName("cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$4e65f4b");

将Object的say方法 代理 给了 serviceImpl$$EnhancerByCGLIB$$4e65f4b的 CGLIB$say$0方法

即表明,c1的say方法转发到了c2的CGLIB$say$0方法

思考

1.fastclass比反射快的原因
通过方法前面或者标识符index,利用switch case直接利用对象去调用函数
而反射是java.lang.reflect.Method#invoke,稍微复杂点,这个没研究过具体实现

2.MethodProxy#invoke和MethodProxy#invokeSuper什么区别,即[c1,f1]与[c2,f2]的区别
[c1,f1]对应的是 父类的class和fastclass
[c2,f2]对应的是 父类的enhanceClass和 enhanceFastClass

3.MethodProxy#init创建fastclass时,每个method在第一次调用时,都会进行
net.sf.cglib.proxy.MethodProxy#init
net.sf.cglib.proxy.MethodProxy#helper
net.sf.cglib.reflect.FastClass.Generator#create
那么为什么对应的fastclass文件只生成了一次(不是一个method调用一次就生成一次)
并且一次就有整个类的信息,而不是只有这个method相关信息呢

第一点:同一个类的fastClass只生成了一次,

net.sf.cglib.reflect.FastClass.Generator#create
net.sf.cglib.core.AbstractClassGenerator#create
里面用了缓存

第二点:一次就有整个类的信息,而不是只有这个method信息
net.sf.cglib.proxy.MethodProxy#create时就传入和class c1,c2
后来创建fastClass时
net.sf.cglib.proxy.MethodProxy#helper
调用了g.setType(type);
在fastClass生成时
net.sf.cglib.reflect.FastClass.Generator#generateClass
用到了这个之前设置好的Class type,也就直到类信息了
invokeSuper的逻辑

4.把invokeSuper改成invoke会怎么样
结论:死循环,堆栈溢出
原因分析:就相当于MethodProxy方法代理中,并没有代理
看c1.say方法,上一节有列举出来

final void say() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            methodInterceptor.intercept(this, CGLIB$say$0$Method, CGLIB$emptyArgs, CGLIB$say$0$Proxy);
        } else {
            say();
        }
    }

这里会走到methodInterceptor.intercept,会进过aop
本来调用invokeSuper,在这里如果改成了invoke,那么

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

会用f1(之前invokeSuper用的f2),f1的代码参照上面列举出来的对应逻辑

//getIndex中say方法前面返回0作为index

public Object invoke(int i, Object obj, Object[] objArr) throws InvocationTargetException {
        InvocationTargetException invocationTargetException;
        serviceImpl cglib_CglibLearn_serviceImpl = (serviceImpl) obj;
        switch (i) {
            case 0:
                try {
                    cglib_CglibLearn_serviceImpl.say();
                    return null;
                } catch (Throwable th) {
                    invocationTargetException = new InvocationTargetException(th);
                }

也就是说serviceImpl.say()通过invoke(非invokeSuper)最终还是回到了serviceImpl.say(),变成了递归导致堆栈溢出。

5.methodProxy和fastClass结合使用
methodProxy用于生成方法代理的关系绑定(classA.methodA被classB.methodB代理)
fastClass用于完成方法代理的快速调用,通过签名拿到标识index,避免重复反射

吐槽

1.methodProxy负责了fastClass的生成,但是methodProxy多次调用生成fastClass,还要让fastClass最终只有一份class文件
也就是调用代理方法时,再创建fastEnhance类,再转发过去
这种lazy的思想感觉超出了自己的职责

2.c1,c2,f1,f2的关系有点绕

3.文档少

4.反编译用http://www.javadecompilers.com/
方式选择CFR (very good and well-supported decompiler for Java 8)
不要选Jadx, fast and with Android support
否则代码会让人误解,比如

//cglib_CglibLearn_serviceImpl__EnhancerByCGLIB__b2e6ff51.CGLIB$say$0()函数在不同反编译方式下

//CFR (very good and well-supported decompiler for Java 8)方式

    final void CGLIB$say$0() {
        super.say();//即CglibLearn.serviceImpl.say()方法
    }

//Jadx, fast and with Android support
    final void CGLIB$say$0() {
        say();//super没了!!!让我以为invokeSuper函数最终也会产生递归!!!
    }

refer

http://www.cnblogs.com/cruze/p/3865180.html



转摘于:https://www.jianshu.com/p/001f866a49d7

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值