天天看点

cglib创建代理对象(3)

MethodProxy调用分析

代码的开始,还是从第一篇的那个例子开始,代码我就不贴了。直接看MethodProxy

他是在MethodInterceptor里面的入参,通过它可以调用原始的方法,也可以调用父类的方法,也可以调用同一个类型的不同对象。

重点就是最后一个,他是可以调用同一个类型的不同对象的。

方法分析

create(Class, Class, String, String, String)

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

它是一个静态的方法,给Enhancer使用的,通过它可以创建MethodProxy。对应代理类里面的代码如下图所示

cglib创建代理对象(3)

(这个代理类就是一开始测试的例子)。

需要注意的是它的几个参数:对比上面的图片来看

  1. Class c1:被代理类的class对象
  2. Class c2:代理类的class对象
  3. String desc:方法的描述信息(方法的参数,异常)
  4. String name1:被代理类的同一个描述信息的方法的名字。
  5. String name2:代理类的同一个描述信息的方法的名字。

对比这些入参,看一下MethodProxy的属性的含义:

属性解释

private Signature sig1;
    private Signature sig2;
    private CreateInfo createInfo;
    
    private final Object initLock = new Object();
    private volatile FastClassInfo fastClassInfo;
           
  1. Signature sig1: 被代理类的方法签名
  2. Signature sig2: 代理类的方法签名
  3. CreateInfo createInfo: 它是一个创建代理对象的上下文对象,c1表示被代理的类,c2表示代理类,剩下的三个就是从当前创建代理对象的上下文中获取属性值而已。
cglib创建代理对象(3)
  1. FastClassInfo fastClassInfo: 会之后再init方法里面会创建。它就是之后通过

    Signature

    CreateInfo

    创建出来的类的引用。他有两个属性,一个是代理类,一个被代理类的。
cglib创建代理对象(3)
  1. Object initLock: 锁

invokeSuper

通过MethodProxy调用父类方法。

Object obj: 代理类

Object[]:方法的入参。

再init方法里面会创建两个FastClass的子类(下面会分析),之后,会调用基于代理类生成的FastClass的invoke方法,这里面会做真正的方法调用。

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方法,来做初始化。先看init方法

init方法

通过CreateInfo和Signature创建FastClassInfo对象。

在创建FastClassInfo的时候,会动态的生成两个类。他们会继承FastClass对象,并且赋值为FastClassInfo的f1和f2属性,然后再生成的类里面通过方法的签名找下标,赋值为FastClassInfo的i1和i2属性。这个下标对应的生成的FastClass里面getIndex方法返回的值。

也就是FastClassInfo的f1和f2的属性为代理类和被代理类生成的FastClass对象,i1和i2保存的是这次调用方法再各自的FastClass对象里面的下标。

这个下标是生成的类的

getIndex

方法返回的下标,至于这个方法长什么样子,继续往下看。

private void init()
    {
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {   // double check
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;

                    FastClassInfo fci = new FastClassInfo();
                    // 创建FastClass类,被代理的类
                    fci.f1 = helper(ci, ci.c1);
                    // 代理的类
                    fci.f2 = helper(ci, ci.c2);
                    // 直接调用方法,调用的是f1的getIndex方法,方法的签名是被代理类再此次调用里面的方法的签名。
                    fci.i1 = fci.f1.getIndex(sig1);
                     // 同样
                    fci.i2 = fci.f2.getIndex(sig2);
                    // 赋值
                    fastClassInfo = fci;
                   // 变为null的目的是为了防止再次初始化。直接把报错都比悄无声息的出错好。
                    createInfo = null;
                }
            }
        }
    }
           

重点就是

helper

方法了,这方法里面会通过Cglib来生成类,它肯定有一个内部类,然后继承于

AbstractClassGenerator

,实现

generateClass

方法,然后自己通过

ClassEmitter

的子类来编写这个类。

Help方法分析

private static FastClass helper(CreateInfo ci, Class type) {
        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();
    }
           

显然是继承了

AbstractClassGenerator

,关于它里面是怎么做的,我就不分析了。因为我也不太懂。最后直接看生成的FastClass长什么样子,可以看一点东西,比如图Soruce,Key,🤣。

invoke方法

和上面的

invokeSuper

一样,一上来先是init,但是它调用的是FastClassInfo这个i不过的f1的invoke方法。(也就是被代理类生成的FastClass)

通过上面的分析,知道FastClassInfo的含义了,FastClassInfo里面有四个属性,在MethodProxy里面会创建两个动态的类,这两个类都是继承于FastClass的,FastClassInfo中的f1和f2属性对应的是被代理的类和代理类对应生成的FastClass的引用。i1和i2对应的是此次调用的方法在生成的FastClass类中getIndex方法返回的值。

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

除了上面说的这两个方法之外,MethodProxy还有几个方法,下面我也列举出说明一下。

  • getSignature:返回被代理对象的被代理方法的签名
  • getSuperName:方法代理对象(通过cglib生成的)没有拦截器的直接调用super的方法的签名
  • getSuperIndex:返回的是为代理对象生成的FastClass中,通过getIndex返回的下标。
  • find:返回给定类型的方法签名。

这些方法在日常的使用的机会是比较少的。重点还是上面说的方法

说到这里,就剩下看看生成的FastClass长什么样子了?

生成的FastClass长什么样子?

  1. 为被代理类生成的FastClass
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.sf.cglib.samples.simple;

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

public class Bean$$FastClassByCGLIB$$62952930 extends FastClass {
    public Bean$$FastClassByCGLIB$$62952930(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1816210712:
            if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
                return 5;
            }
            break;
        case -1123095605:
            if (var10000.equals("hhhh()Ljava/lang/String;")) {
                return 3;
            }
            break;
        case -908043773:
            if (var10000.equals("sasasasa()Ljava/lang/String;")) {
                return 2;
            }
            break;
        case -286557062:
            if (var10000.equals("lipu1()Ljava/lang/String;")) {
                return 0;
            }
            break;
        case 580486146:
            if (var10000.equals("dadada()Ljava/lang/String;")) {
                return 6;
            }
            break;
        case 1631032358:
            if (var10000.equals("lululu()Ljava/lang/String;")) {
                return 1;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 7;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 8;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 9;
            }
            break;
        case 2130387705:
            if (var10000.equals("jump()Ljava/lang/String;")) {
                return 4;
            }
        }

        return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.String")) {
                        return 5;
                    }
                }
            }
            break;
        case -1776922004:
            if (var1.equals("toString")) {
                switch(var2.length) {
                case 0:
                    return 8;
                }
            }
            break;
        case -1339395145:
            if (var1.equals("dadada")) {
                switch(var2.length) {
                case 0:
                    return 6;
                }
            }
            break;
        case -1295482945:
            if (var1.equals("equals")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.Object")) {
                        return 7;
                    }
                }
            }
            break;
        case -1091633701:
            if (var1.equals("lululu")) {
                switch(var2.length) {
                case 0:
                    return 1;
                }
            }
            break;
        case 3201536:
            if (var1.equals("hhhh")) {
                switch(var2.length) {
                case 0:
                    return 3;
                }
            }
            break;
        case 3273774:
            if (var1.equals("jump")) {
                switch(var2.length) {
                case 0:
                    return 4;
                }
            }
            break;
        case 102979631:
            if (var1.equals("lipu1")) {
                switch(var2.length) {
                case 0:
                    return 0;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 9;
                }
            }
            break;
        case 2133693496:
            if (var1.equals("sasasasa")) {
                switch(var2.length) {
                case 0:
                    return 2;
                }
            }
        }

        return -1;
    }

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

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        Bean var10000 = (Bean)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return var10000.lipu1();
            case 1:
                return var10000.lululu();
            case 2:
                return var10000.sasasasa();
            case 3:
                return var10000.hhhh();
            case 4:
                return var10000.jump();
            case 5:
                return var10000.sayHello((String)var3[0]);
            case 6:
                return var10000.dadada();
            case 7:
                return new Boolean(var10000.equals(var3[0]));
            case 8:
                return var10000.toString();
            case 9:
                return new Integer(var10000.hashCode());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        Bean var10000 = new Bean;
        Bean var10001 = var10000;
        int var10002 = var1;

        try {
            switch(var10002) {
            case 0:
                var10001.<init>();
                return var10000;
            }
        } catch (Throwable var3) {
            throw new InvocationTargetException(var3);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public int getMaxIndex() {
        return 9;
    }
}

           
  1. 为代理类生成的FastClass
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package net.sf.cglib.samples.simple;

import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.samples.simple.Bean..EnhancerByCGLIB..d79f67a6;

public class Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec extends FastClass {
    public Bean$$EnhancerByCGLIB$$d79f67a6$$FastClassByCGLIB$$cf2d2fec(Class var1) {
        super(var1);
    }

    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -2144987997:
            if (var10000.equals("CGLIB$lipu1$0()Ljava/lang/String;")) {
                return 18;
            }
            break;
        case -2055565910:
            if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 11;
            }
            break;
        case -1816210712:
            if (var10000.equals("sayHello(Ljava/lang/String;)Ljava/lang/String;")) {
                return 16;
            }
            break;
        case -1457535688:
            if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                return 20;
            }
            break;
        case -1123095605:
            if (var10000.equals("hhhh()Ljava/lang/String;")) {
                return 14;
            }
            break;
        case -908043773:
            if (var10000.equals("sasasasa()Ljava/lang/String;")) {
                return 13;
            }
            break;
        case -894172689:
            if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 3;
            }
            break;
        case -623122092:
            if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
                return 19;
            }
            break;
        case -419626537:
            if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 6;
            }
            break;
        case -286557062:
            if (var10000.equals("lipu1()Ljava/lang/String;")) {
                return 7;
            }
            break;
        case 560567118:
            if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
                return 8;
            }
            break;
        case 580486146:
            if (var10000.equals("dadada()Ljava/lang/String;")) {
                return 17;
            }
            break;
        case 811063227:
            if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 5;
            }
            break;
        case 973717575:
            if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
                return 10;
            }
            break;
        case 1221173700:
            if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 4;
            }
            break;
        case 1230699260:
            if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
                return 9;
            }
            break;
        case 1584330438:
            if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 12;
            }
            break;
        case 1631032358:
            if (var10000.equals("lululu()Ljava/lang/String;")) {
                return 21;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 0;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 1;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 2;
            }
            break;
        case 2130387705:
            if (var10000.equals("jump()Ljava/lang/String;")) {
                return 15;
            }
        }

        return -1;
    }

    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.String")) {
                        return 16;
                    }
                }
            }
            break;
        case -1776922004:
            if (var1.equals("toString")) {
                switch(var2.length) {
                case 0:
                    return 1;
                }
            }
            break;
        case -1339395145:
            if (var1.equals("dadada")) {
                switch(var2.length) {
                case 0:
                    return 17;
                }
            }
            break;
        case -1295482945:
            if (var1.equals("equals")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("java.lang.Object")) {
                        return 0;
                    }
                }
            }
            break;
        case -1091633701:
            if (var1.equals("lululu")) {
                switch(var2.length) {
                case 0:
                    return 21;
                }
            }
            break;
        case -1053468136:
            if (var1.equals("getCallbacks")) {
                switch(var2.length) {
                case 0:
                    return 10;
                }
            }
            break;
        case -60403779:
            if (var1.equals("CGLIB$SET_STATIC_CALLBACKS")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 12;
                    }
                }
            }
            break;
        case 3201536:
            if (var1.equals("hhhh")) {
                switch(var2.length) {
                case 0:
                    return 14;
                }
            }
            break;
        case 3273774:
            if (var1.equals("jump")) {
                switch(var2.length) {
                case 0:
                    return 15;
                }
            }
            break;
        case 85179481:
            if (var1.equals("CGLIB$SET_THREAD_CALLBACKS")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 11;
                    }
                }
            }
            break;
        case 102979631:
            if (var1.equals("lipu1")) {
                switch(var2.length) {
                case 0:
                    return 7;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 2;
                }
            }
            break;
        case 161998109:
            if (var1.equals("CGLIB$STATICHOOK1")) {
                switch(var2.length) {
                case 0:
                    return 20;
                }
            }
            break;
        case 495524492:
            if (var1.equals("setCallbacks")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 6;
                    }
                }
            }
            break;
        case 1154623345:
            if (var1.equals("CGLIB$findMethodProxy")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("net.sf.cglib.core.Signature")) {
                        return 19;
                    }
                }
            }
            break;
        case 1264770776:
            if (var1.equals("CGLIB$lipu1$0")) {
                switch(var2.length) {
                case 0:
                    return 18;
                }
            }
            break;
        case 1811874389:
            if (var1.equals("newInstance")) {
                switch(var2.length) {
                case 1:
                    String var10001 = var2[0].getName();
                    switch(var10001.hashCode()) {
                    case -845341380:
                        if (var10001.equals("net.sf.cglib.proxy.Callback")) {
                            return 3;
                        }
                        break;
                    case 1730110032:
                        if (var10001.equals("[Lnet.sf.cglib.proxy.Callback;")) {
                            return 4;
                        }
                    }
                case 2:
                default:
                    break;
                case 3:
                    if (var2[0].getName().equals("[Ljava.lang.Class;") && var2[1].getName().equals("[Ljava.lang.Object;") && var2[2].getName().equals("[Lnet.sf.cglib.proxy.Callback;")) {
                        return 5;
                    }
                }
            }
            break;
        case 1817099975:
            if (var1.equals("setCallback")) {
                switch(var2.length) {
                case 2:
                    if (var2[0].getName().equals("int") && var2[1].getName().equals("net.sf.cglib.proxy.Callback")) {
                        return 8;
                    }
                }
            }
            break;
        case 1905679803:
            if (var1.equals("getCallback")) {
                switch(var2.length) {
                case 1:
                    if (var2[0].getName().equals("int")) {
                        return 9;
                    }
                }
            }
            break;
        case 2133693496:
            if (var1.equals("sasasasa")) {
                switch(var2.length) {
                case 0:
                    return 13;
                }
            }
        }

        return -1;
    }

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

    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        d79f67a6 var10000 = (d79f67a6)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return new Boolean(var10000.equals(var3[0]));
            case 1:
                return var10000.toString();
            case 2:
                return new Integer(var10000.hashCode());
            case 3:
                return var10000.newInstance((Callback)var3[0]);
            case 4:
                return var10000.newInstance((Callback[])var3[0]);
            case 5:
                return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 6:
                var10000.setCallbacks((Callback[])var3[0]);
                return null;
            case 7:
                return var10000.lipu1();
            case 8:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 9:
                return var10000.getCallback(((Number)var3[0]).intValue());
            case 10:
                return var10000.getCallbacks();
            case 11:
                d79f67a6.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                return null;
            case 12:
                d79f67a6.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                return null;
            case 13:
                return var10000.sasasasa();
            case 14:
                return var10000.hhhh();
            case 15:
                return var10000.jump();
            case 16:
                return var10000.sayHello((String)var3[0]);
            case 17:
                return var10000.dadada();
            case 18:
                return var10000.CGLIB$lipu1$0();
            case 19:
                return d79f67a6.CGLIB$findMethodProxy((Signature)var3[0]);
            case 20:
                d79f67a6.CGLIB$STATICHOOK1();
                return null;
            case 21:
                return var10000.lululu();
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        d79f67a6 var10000 = new d79f67a6;
        d79f67a6 var10001 = var10000;
        int var10002 = var1;

        try {
            switch(var10002) {
            case 0:
                var10001.<init>();
                return var10000;
            }
        } catch (Throwable var3) {
            throw new InvocationTargetException(var3);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

    public int getMaxIndex() {
        return 21;
    }
}
           

说明

FastClass本身是一个抽象类,生成的子类实现了他的抽象方法,主要有下面的几个方法

  1. getIndex:

    在这个方法里面会通过传递进来的要创建的类型,获取他的所有方法,组成一个很大的switch,case。通过传递进来的方法的签名的hash值,返回对应的下标,这个下标和invoke里面用的是一样的。具体的可以看上面的

    public int getIndex(Signature var1)

    方法
cglib创建代理对象(3)
  1. invoke

    先看他的方法的入参:

cglib创建代理对象(3)

int var1:表示传递过来的index,这个index就是getIndex返回的。

Object var2:表示需要调用的对象。

Object[] var3: 方法的入参。

从代码来看这里很简单,先强转为调用的类型(如上面的(Bean)var2),之后通过入参(var,其实就是index)调用具体的方法。

代理类和被代理的类都是同样的逻辑,只不过强转的时候类型不一致,别的操作都是一致的。

总结

在构建代理类的时候,如果拦截器的类型是MethodIntercept,就会创建针对MethodIntercept使用的方法的MethodProxy,在调用MethodIntercept的时候,会将MethodProxy传递过去。MethodProxy是在生成的代理类的静态代码块里面初始化的。(通过MethodProxy#create方法)。

创建MethodProxyd的时候会传递四个参数,分为对应,被代理的类对象的class对象,代理类对象的class对象,MethodIntercepted起作用的方法的签名,这个方法对应在被代理类和代理类中的名字。通过这几个参数给MethodProxy的几个属性赋值,同时他们也有不同的语义。Cglib用Signature来表示方法的签名(方法的名字和方法的描述信息),对应MethodProxy的sig1(被代理类的方法签名)和sig2(代理类的方法签名)属性。

此外,在MethodProxy还有一个FastClassInfo属性,Cglib里面在调用MethodProxy#invoke或者invokeSuper的时候,会针对sig1和sig2动态的生成两个FastClass的子类,在这子类里面会获取要实现的类型的所有的方法,利用这些方法做调用,在生成的FastClass的子类里面主要有两个方法,getIndex和invoke方法。在getIndex里面会利用方法的签名返回index,这些index对应的是该方法在invoke里面的调用下标。

在invoke方法里面将,传递进来的对象强转为它获取方法类型的class对象,通过下标,做方法调用。

简单的说就是,MethodProxy在调用的时候,会动态的生成两个继承于FastClass类,它里面有俩重要的方法,getindex和invoke方法,在类里面会获取到要实现类的class的所有的方法,这些方法在invoke里面会组成从0开始的方法调用的下标。并且会将invoke方法传递进来的对象强转为要实现的类的类型,通过下标做匹配,做方法调用。在getIndex方法里面,通过传递进来的方法签名的hashcode,做匹配,返回index(这个index是对应的invoke方法里面的index)。

可以看到,在invoke方法里面,传递进去的主要是生成的FastClass要实现的类的子类,都是可以的。invoke里面有强转操作。

方法调用分析

上面分析了MethodProxy的属性和FastClass的作用。这一章节,聚焦到方法的调用上。这里说的方法调用是通过MethodIntercepted做调用的。通过前面的文章,前面的分析已经知道,剩余的几个callback的子接口是没有调用父类的操作的。是没有MethodProxy的。还是从一个例子开始。

重新来一个例子,看这个例子,就一个callback(MethodInterceptor),有TestBean和TestBean1,俩关系是泛化。并且子类重写了父类的test1方法。在TestBean中是有方法嵌套调用的。

下面正对invokeSupe和invoke不同的调用方式做分析。

public class TestMain {
    public static void main(String[] args) {
        final TestBean testBean = new TestBean();
        final  TestBean1 testBean1 = new TestBean1();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestBean.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("intercept:" + method.getName());
               return proxy.invokeSuper(obj,args);
            }
        });
        TestBean o = (TestBean)enhancer.create();
        System.out.println(o.test("param"));
    }
}
class TestBean{

    public String test(String param){
        String s = test1(param);
        return "test:" + s;
    }

    public String test1(String param){
        return "test1:" + param;
    }
}
class TestBean1 extends TestBean{
    @Override
    public String test1(String param) {
        return "TestBean1:test1";
    }
}
           

invokeSuper

结果

cglib创建代理对象(3)

方法嵌套的调用被发现了。

分析:

想想上面生成的FastClass对象,在

invokeSuper

得时候,调用的是

MethodProxy#FastClassInfo#f2的invoke方法

MethodProxy在代理类里面创建得时候传递的是。

cglib创建代理对象(3)

从这可以看出来,getIndex传递的是

CGLIB$test1$1

,调用invokeSuper的时候,会调用到动态生成的FastClass里面invoke方法,在invoke里面会强转为代理类,最终会调用到

CGLIB$test1$1

方法里面去。所以,这这就解释了invokeSuper里面传递代理对象进去,因为代理对象才能转换为代理对象。

cglib创建代理对象(3)

最终会调用到

CGLIB$test1$1

(这个方法是Cglib自动生成的。只要这个方法引用的MethodInterceptor,就会多一个MethodProxy和一个没有methodInterceptor的方法,在这个方法里面直接调用super方法。)

CGLIB$test1$1里面是没有调用MethodIntercepter方法的。因为她不能调用,如果调用,就会发生循环调用,一直在调用回调,导致栈溢出。

调用到代理类的父类了,父类里面再次调用了test1方法,test1方法也有代理,所以,又再次走到了上面的逻辑,再次invoke调用到父类的方法。

invokeSuper是可以检测到方法之前的嵌套调用的。

invoke

  1. invoke方法传参为代理对象
    cglib创建代理对象(3)

    栈溢出,溢出的原因是:

    因为在调用invoke的时候,他调用的为是被代理类的生成的fastClass里面的invoke方法。

cglib创建代理对象(3)

代理类肯定是能转为TestBean,直接调用代理类的test方法,这和直接调用没有什么差别,还是会调用到代理类里面,还是会调用到methodInterceptor里面的。一直会循环下去。

  1. invoke方法传参为普通的对象
cglib创建代理对象(3)

调用正常,因为在为被代理类生成的fastclass的invoke方法里面,会强转为TestBean类型,传递进去的testBean就是TestBean。在invoke里面直接强转,然后直接调用。所以,没有问题(Spring中切面的调用就是通过这种方式,所以才会导致所谓的失效,这是本质原因)

  1. invoke方法传递参数为被代理对象的子类对象

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAcC3Vqn-

    cglib创建代理对象(3)
    要注意,Testbean1是继承TestBean的,所以在invoke方法里面也是可以强转为TestBean类型的,但是Testbean1重写了test1方法。所以,在调用的时候就调用到了子类的方法。这种调用方式是符合FastClass#invoke的原本的意思的。

总结

MethodProxy调用 invokeSuper的时候 方法之间的调用是可以发现的。调用invoke的时候,方法之间的嵌套是不能发现的。

并且在,MethodProxy调用的时候,会针对代理类和被代理的类动态的创建两个FastClass的子类,在invoke和invokeSuper的时候,会调用不同的FastClass类里面的方法。

到这里,关于Cglib的东西结束了,主要分析了Cglib的内部创建代理类的过程,和几个有意思的属性,这些属性分别用于缓存,代理对象class名字的生成。还分析了Callback的各种子类,已经他们是在Cgliib中的使用,和Cglib怎么来编写这些方法的。此外。还分析MethodIntercepter中,MethodProxy调用的时相关的代码逻辑。从根上分析了Cglib创建代理对象的时候,失效的问题(主要分为两种,invokeSuper不会,invoke会)。

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

 
    private static FastClass helper(CreateInfo ci, Class type) {
        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();
    }

    private MethodProxy() {
    }

    /**
     * Return the signature of the proxied method.
     */
    public Signature getSignature() {
        return sig1;
    }

    /**
     * Return the name of the synthetic method created by CGLIB which is
     * used by {@link #invokeSuper} to invoke the superclass
     * (non-intercepted) method implementation. The parameter types are
     * the same as the proxied method.
     */
    public String getSuperName() {
        return sig2.getName();
    }

    /**
     * Return the {@link net.sf.cglib.reflect.FastClass} method index
     * for the method used by {@link #invokeSuper}. This index uniquely
     * identifies the method within the generated proxy, and therefore
     * can be useful to reference external metadata.
     * @see #getSuperName
     */
    public int getSuperIndex() {
        init();
        return fastClassInfo.i2;
    }

    // For testing
    FastClass getFastClass() {
      init();
      return fastClassInfo.f1;
    }

    // For testing
    FastClass getSuperFastClass() {
      init();
      return fastClassInfo.f2;
    }

    /**
     * Return the <code>MethodProxy</code> used when intercepting the method
     * matching the given signature.
     * @param type the class generated by Enhancer
     * @param sig the signature to match
     * @return the MethodProxy instance, or null if no applicable matching method is found
     * @throws IllegalArgumentException if the Class was not created by Enhancer or does not use a MethodInterceptor
     */
    public static MethodProxy find(Class type, Signature sig) {
        try {
            Method m = type.getDeclaredMethod(MethodInterceptorGenerator.FIND_PROXY_NAME,
                                              MethodInterceptorGenerator.FIND_PROXY_TYPES);
            return (MethodProxy)m.invoke(null, new Object[]{ sig });
        } catch (NoSuchMethodException e) {
            throw new IllegalArgumentException("Class " + type + " does not use a MethodInterceptor");
        } catch (IllegalAccessException e) {
            throw new CodeGenerationException(e);
        } catch (InvocationTargetException e) {
            throw new CodeGenerationException(e);
        }
    }

    /**
     * Invoke the original method, on a different object of the same type.
     * @param obj the compatible object; recursion will result if you use the object passed as the first
     * argument to the MethodInterceptor (usually not what you want)
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    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;
        }
    }

    /**
     * Invoke the original (super) method on the specified object.
     * @param obj the enhanced object, must be the object passed as the first
     * argument to the MethodInterceptor
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    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();
        }
    }
}

           

到此,结束了。

关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。