代理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
这两个文件的区别是什么

对象 意义,实例
c1 cglib.CglibLearn$serviceImpl
f1 c1的fastClass,更方便调用c1的函数 cglib.CglibLearn$serviceImpl$$FastClassByCGLIB$$4733f381
c2 cglib.CglibLearn$serviceImpl$$EnhancerByCGLIB$$b2e6ff51 即c1的enhance class
f2 c2的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容