天天看點

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

           

到此,結束了。

關于部落格這件事,我是把它當做我的筆記,裡面有很多的内容反映了我思考的過程,因為思維有限,不免有些内容有出入,如果有問題,歡迎指出。一同探讨。謝謝。