1. 程式人生 > >JDK動態代理與CGLIB動態代理應用及原始碼解析

JDK動態代理與CGLIB動態代理應用及原始碼解析

代理模式

代理模式:為其他物件提供一種代理以控制對這個物件的訪問。

代理模式中有三種角色:Subject抽象主題角色、RealSubject真實主題角色、Proxy代理主題角色。Subject描述了業務行為,RealSubject執行具體的業務邏輯,Proxy代理會攔截對RealSubject物件方法的呼叫,並在方法呼叫前後做預處理以及一些善後工作。

代理模式可以很好地在不侵入原始碼的情況下,拓展原來的功能。

下圖為Proxy模式的靜態類圖:
這裡寫圖片描述
下圖為Proxy模式的呼叫關係:
這裡寫圖片描述

動態代理

靜態代理由於硬編碼,難以應對真實物件和呼叫方法靈活多變的情況,動態代理則對這些場景應付自如。

動態代理主要有兩種實現方式:1、JDK自帶的Proxy 2、CGLIB位元組碼生成庫

基於JDK的動態代理

基於JDK的動態代理關鍵在於兩個類:InvocationHandler和Proxy。
其主要實現邏輯是,由InvocationHandler定義方法執行前後的增強邏輯,由Proxy類去生成一個繼承自Proxy並且實現了真實物件介面的新物件–代理物件,對該代理物件的方法呼叫經由InvocationHandler攔截,執行增強邏輯和呼叫真實物件執行業務邏輯。

下面我們先看一個例子:
定義介面:

public interface UserService {
    public
String getName(int id); public Integer getAge(int id); }

定義真實物件:

public class UserServiceImpl implements UserService {

    public UserServiceImpl() {
    }

    @Override
    public String getName(int id) {
        System.out.println("---getName---");
        return "John";
    }

    @Override
public Integer getAge(int id) { System.out.println("---getAge---"); return 10; } }

定義InvocationHandler:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("getName")) {
            System.out.println("+++before get name+++");
            Object res = method.invoke(target, args);
            System.out.println("+++after get name+++");
            return res;
        } else {
            Object res = method.invoke(target, args);
            return res;
        }

    }

    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        UserService us = new UserServiceImpl();
        InvocationHandler ih = new MyInvocationHandler(us);
        UserService usProxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(),
                us.getClass().getInterfaces(), ih);
        System.out.println(usProxy.getName(1));
        System.out.println(usProxy.getAge(1));
        System.out.println(usProxy.getClass());
    }

}

測試結果:
這裡寫圖片描述

可以看到,對於getName方法的增強邏輯執行了。

下面我們從原始碼角度分析一下這個過程的實現原理,先看InvocationHandler:
InvocationHandler是一個介面,只有一個方法:

 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

在定義增強邏輯類時,需要實現該介面,並在invoke方法裡實現增強邏輯和對真實物件方法的呼叫。對於invoke方法的三個引數,proxy表示代理物件,method表示真實物件的方法,args表示真實物件方法的引數,這裡也可以看出對真實物件方法的呼叫是通過反射來實現的。

增強邏輯定義好了以後,我們需要一個代理物件來執行,先看如何產生這個代理物件。代理物件的產生是通過呼叫Proxy類的靜態方法:newProxyInstance,以下原始碼分析部分篇幅原因僅擷取部分程式碼片段。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
        //該方法需要三個引數:ClassLoader確保返回的代理物件和真實物件由同一個類

        載入器載入,interfaces用於定義代理類應該實現的方法,
        invocationHandler用於定義代理類的增強邏輯
    {
        if (h == null) {
            throw new NullPointerException();
        }

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
         //這一行程式碼很關鍵,這裡是獲取代理物件Class物件,Proxy類內部維護了代理
         //物件的快取,如果快取裡有則直接返回,如果沒有,則生成它
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            //由Class物件獲取構造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                //最後由構造器返回新的代理物件例項
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

接下來我們看代理物件的Class物件是如何生成的,快取邏輯略去不表,直接看沒有快取的情況,代理類Class物件由ProxyClassFactory工廠生成:

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
       //驗證程式碼略去
            /*
             * Generate the specified proxy class.
             */
             //這裡生成了代理物件二進位制位元組碼流
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);
            try {
                //通過二進位制位元組碼流載入代理類
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

最終生成二進位制位元組碼流用到了Sun的ProxyGenerator類,反編譯:

public static byte[] generateProxyClass(String s, Class aclass[])
    {
        ProxyGenerator proxygenerator = new ProxyGenerator(s, aclass);
        //該方法生成代理物件的二進位制位元組碼流
        byte abyte0[] = proxygenerator.generateClassFile();
        //如果要儲存該位元組碼檔案,可以將其寫到硬碟上,我們稍後分析
        if(saveGeneratedFiles)
            //儲存檔案部分略去
        return abyte0;
    }

具體生成原始碼如下:

private byte[] generateClassFile() {
        addProxyMethod(hashCodeMethod, java / lang / Object);
        addProxyMethod(equalsMethod, java / lang / Object);
        addProxyMethod(toStringMethod, java / lang / Object);
        for (int i = 0; i < interfaces.length; i++) {
            Method amethod[] = interfaces[i].getMethods();
            for (int k = 0; k < amethod.length; k++)
                addProxyMethod(amethod[k], interfaces[i]);

        }

        List list;
        for (Iterator iterator = proxyMethods.values().iterator(); iterator.hasNext(); checkReturnTypes(list))
            list = (List) iterator.next();

        try {
            methods.add(generateConstructor());
            for (Iterator iterator1 = proxyMethods.values().iterator(); iterator1.hasNext();) {
                List list1 = (List) iterator1.next();
                Iterator iterator2 = list1.iterator();
                while (iterator2.hasNext()) {
                    ProxyMethod proxymethod = (ProxyMethod) iterator2.next();
                    fields.add(new FieldInfo(proxymethod.methodFieldName, "Ljava/lang/reflect/Method;", 10));
                    methods.add(proxymethod.generateMethod());
                }
            }

            methods.add(generateStaticInitializer());
        } catch (IOException ioexception) {
            throw new InternalError("unexpected I/O Exception");
        }
        if (methods.size() > 65535)
            throw new IllegalArgumentException("method limit exceeded");
        if (fields.size() > 65535)
            throw new IllegalArgumentException("field limit exceeded");
        cp.getClass(dotToSlash(className));
        cp.getClass("java/lang/reflect/Proxy");
        for (int j = 0; j < interfaces.length; j++)
            cp.getClass(dotToSlash(interfaces[j].getName()));

        cp.setReadOnly();
        ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
        DataOutputStream dataoutputstream = new DataOutputStream(bytearrayoutputstream);
        try {
            dataoutputstream.writeInt(-889275714);
            dataoutputstream.writeShort(0);
            dataoutputstream.writeShort(49);
            cp.write(dataoutputstream);
            dataoutputstream.writeShort(49);
            dataoutputstream.writeShort(cp.getClass(dotToSlash(className)));
            dataoutputstream.writeShort(cp.getClass("java/lang/reflect/Proxy"));
            dataoutputstream.writeShort(interfaces.length);
            for (int l = 0; l < interfaces.length; l++)
                dataoutputstream.writeShort(cp.getClass(dotToSlash(interfaces[l].getName())));

            dataoutputstream.writeShort(fields.size());
            FieldInfo fieldinfo;
            for (Iterator iterator3 = fields.iterator(); iterator3.hasNext(); fieldinfo.write(dataoutputstream))
                fieldinfo = (FieldInfo) iterator3.next();

            dataoutputstream.writeShort(methods.size());
            MethodInfo methodinfo;
            for (Iterator iterator4 = methods.iterator(); iterator4.hasNext(); methodinfo.write(dataoutputstream))
                methodinfo = (MethodInfo) iterator4.next();

            dataoutputstream.writeShort(0);
        } catch (IOException ioexception1) {
            throw new InternalError("unexpected I/O Exception");
        }
        return bytearrayoutputstream.toByteArray();
    }

至此,總結一下,Proxy類通過ProxyClassFactory生成了繼承Proxy類並實現了真實物件介面的 $ProxyX代理物件的二進位制位元組碼流,並載入該位元組碼返回代理物件Class物件,由該Class物件經反射得到構造器構造了代理物件的例項。
在原始碼中我們可以通過saveGeneratedFiles變數儲存生成的class檔案,我們反編譯上面的示例生成的class檔案:

public final class $Proxy0 extends Proxy implements UserService {

    private static Method m1;
    private static Method m4;
    private static Method m0;
    private static Method m3;
    private static Method m2;

    public $Proxy0(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

    public final boolean equals(Object obj) {
        try {
            return ((Boolean) super.h.invoke(this, m1, new Object[]{obj})).booleanValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
//這裡是UserService介面的getAge方法,在代理物件$Proxy0上的呼叫被invocationHandler攔截,
//經由其invoke方法呼叫(即我們之前定義的MyinvocationHandler的invoke方法),
//留意invoke方法的引數,我們在MyinvocationHandler裡定義invoke方法時並沒有使用proxy引數,
//這裡proxy引數的位置傳入了this變數,即代理物件本身。
    public final Integer getAge(int i) {
        try {
            return (Integer) super.h.invoke(this, m4, new Object[]{Integer.valueOf(i)});
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer) super.h.invoke(this, m0, null)).intValue();
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String getName(int i) {
        try {
            return (String) super.h.invoke(this, m3, new Object[]{Integer.valueOf(i)});
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String) super.h.invoke(this, m2, null);
        } catch (Error _ex) {
        } catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m4 = Class.forName("cn.john.test.dynamicProxy.UserService").getMethod("getAge", new Class[]{Integer.TYPE});
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m3 = Class.forName("cn.john.test.dynamicProxy.UserService").getMethod("getName", new Class[]{Integer.TYPE});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
        } catch (NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        } catch (ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

基於CGLIB的動態代理

使用示例

public class UserServiceB {
    public String getName(int id) {
        System.out.println("---getName---");
        return "John";
    }
    public Integer getAge(int id) {
        System.out.println("---getAge---");
        return 10;
    }
    public static void main(String[] args) throws InterruptedException, IOException {
    //將cglib生成的Class物件寫成檔案存入硬碟,後面反編譯出來用以分析
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
        UserServiceB us = new UserServiceB();
        // 定義增強器
        Enhancer en = new Enhancer();
        // 定義要代理的物件
        en.setSuperclass(us.getClass());
        // 定義回撥函式
        en.setCallback(new MethodInterceptor() {
            //這裡要理解intercept方法的幾個引數代表的意思
            //obj指的是代理類物件
            //Method指的是 目標類中被攔截的方法
            //args指的是 呼叫攔截方法所需的引數
            //MethodProxy指的是用來呼叫目標類被攔截方法的方法,這個方法比反射更快
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                System.out.println("-----before-------");
                //這裡只能用invokeSuper,原因稍後解釋
                methodProxy.invokeSuper(obj, args);
                System.out.println("-----after--------");
                return null;
            }

        });
        // 生成代理物件
        UserServiceB usb = (UserServiceB) en.create();
        // 在代理物件上呼叫方法
        usb.getName(1);

    }

}

執行結果:
這裡寫圖片描述

同時生成了三個class檔案:一個是代理類,一個是代理類的FastClass,一個是目標類的FastClass
這裡寫圖片描述

原始碼分析

代理類原始碼概覽

從上面的示例程式碼中,我們知道通過cglib生成代理類只需要一個目標類和一個回撥函式(增強邏輯),下面我們從在代理物件上呼叫getName()方法出發,一步一步分析cglib動態代理的實現原理。

先來看生成的代理物件Class檔案反編譯後的原始碼(程式碼很長,代理了Object中的finalize,equals, toString,hashCode,clone方法,這裡僅留下getName()方法):

public class UserServiceB$$EnhancerByCGLIB$$a33459ad extends UserServiceB implements Factory {
    //cglib基於繼承,可以看到代理類繼承了目標類,並實現了Factory介面,Factory介面可以簡化回撥函式的變更
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;//回撥函式
    private static final Method CGLIB$getName$0$Method;//目標類的getName方法
    private static final MethodProxy CGLIB$getName$0$Proxy;//getName方法的代理方法
    //初始化變數
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        //代理類
        Class class_ = Class.forName("cn.john.test.dynamicProxy.UserServiceB$$EnhancerByCGLIB$$a33459ad");
        //目標類
        Class class_2 = Class.forName("cn.john.test.dynamicProxy.UserServiceB");
        Method[] arrmethod = ReflectUtils.findMethods(
                (String[]) new String[]{"getName", "(I)Ljava/lang/String;", "getAge", "(I)Ljava/lang/Integer;"},
                (Method[]) class_2.getDeclaredMethods());
        //getName方法
        CGLIB$getName$0$Method = arrmethod[0];
        //建立更快的方法索引訪問方式,這裡通過目標類和代理類的Class物件和方法簽名為這兩個物件
        //的所有方法都建立了索引,並提供了通過索引呼叫的方法。
        //這裡CGLIB$getName$0$Proxy提供在目標類(或者相容類)上快速呼叫getName和在代理類上
        //快速呼叫CGLIB$getName$0的功能
        CGLIB$getName$0$Proxy = MethodProxy.create(class_2, class_, (String) "(I)Ljava/lang/String;",
                (String) "getName", (String) "CGLIB$getName$0");
    }

    //直接呼叫了目標類的方法,這個方法即用來執行目標類真實邏輯,通過索引呼叫來快速訪問該方法
    final String CGLIB$getName$0(int n) {
        return super.getName(n);
    }

    //代理方法getName入口
    public final String getName(int n) {
    //先獲取回撥函式
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BIND_CALLBACKS((Object) this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        //獲取了回撥函式之後
        if (methodInterceptor != null) {
        //呼叫回撥函式的攔截方法。
        //注意這裡傳入的引數,代理類傳入的this,被攔截的方法傳入的是目標類物件的
        //被攔截的方法,methodProxy傳入的是getName方法的快速訪問代理
            return (String) methodInterceptor.intercept((Object) this, CGLIB$getName$0$Method,
                    new Object[]{new Integer(n)}, CGLIB$getName$0$Proxy);
        }
        return super.getName(n);
    }

    public UserServiceB$$EnhancerByCGLIB$$a33459ad() {
        UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = this;
        UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BIND_CALLBACKS((Object) userServiceB$$EnhancerByCGLIB$$a33459ad);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] arrcallback) {
        CGLIB$THREAD_CALLBACKS.set(arrcallback);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] arrcallback) {
        CGLIB$STATIC_CALLBACKS = arrcallback;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object object) {
        UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = (UserServiceB$$EnhancerByCGLIB$$a33459ad) ((Object) object);
        if (!userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BOUND) {
            userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$BOUND = true;
            Object t = CGLIB$THREAD_CALLBACKS.get();
            if (t != null || (v13465 = CGLIB$STATIC_CALLBACKS) != null) {
                userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) t)[0];
            }
        }
    }
    static {
        UserServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$STATICHOOK1();
    }
}

進入回撥函式後,跟JDK動態代理InvocationHandler一樣,先執行前置增強邏輯,然後將目標類的真實邏輯。注意此處目標類的真實邏輯執行cglib的實現方式與JDK實現方式不同:JDK使用的是反射技術,而cglib則使用了FastClass構建方法索引+繼承的方式訪問目標類的方法。

建立getName方法和CGLIB$getName0的索引
CGLIB$getName$0$Proxy = MethodProxy.create(class_2, class_, (String) "(I)Ljava/lang/String;",
                (String) "getName", (String) "CGLIB$getName$0");
                //注意這裡class_2是目標類,class_是代理類

我們擷取MethodProxy類的部分程式碼:

    private static class CreateInfo {
        Class c1;
        Class c2;
        NamingPolicy namingPolicy;
        GeneratorStrategy strategy;
        boolean attemptLoad;

        public CreateInfo(Class c1, Class c2) {
            this.c1 = c1;
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if (fromEnhancer != null) {
                this.namingPolicy = fromEnhancer.getNamingPolicy();
                this.strategy = fromEnhancer.getStrategy();
                this.attemptLoad = fromEnhancer.getAttemptLoad();
            }
        }
    }
//注意在MethodProxy類內部變數命名:這裡目標類為1,而代理類變為了2
    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
    //準備資料階段,目標類和代理類的Class物件,需要對映的方法簽名,這裡並不會
    //觸發實際上的建立方法索引
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new CreateInfo(c1, c2);
        return proxy;
    }

實際建立方法索引發生在回撥函式中方法呼叫時(Lazyinit):

//MethodProxy類中的InvokeSuper
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();//實際的方法索引在此時建立
            FastClassInfo fci = this.fastClassInfo;
            //在代理類FastClass物件上呼叫索引為i2的方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }
//內部類FastClassInfo
private static class FastClassInfo {
        FastClass f1;//目標類fc
        FastClass f2;//代理類fc
        int i1;//目標類getName方法索引
        int i2;//代理類CGLIB$getName$0方法索引

        private FastClassInfo() {
        }
    }

    private void init() {
        if (this.fastClassInfo == null) {
            Object object = this.initLock;
            synchronized (object) {
                if (this.fastClassInfo == null) {
                    CreateInfo ci = this.createInfo;
                    FastClassInfo fci = new FastClassInfo();
                    //這裡通過位元組碼技術生成目標類和代理類的FastClass類的例項
                    //FastClass關鍵有兩類方法:
                    //一類是getIndex方法,通過方法簽名獲取某方法的索引
                    //一類是invoke方法,通過方法的索引來找到方法並呼叫
                    fci.f1 = MethodProxy.helper(ci, ci.c1);//生成目標類的FastClass物件
                    fci.f2 = MethodProxy.helper(ci, ci.c2);//生成代理類的FastClass物件
                    //獲取目標類和代理類的方法索引
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;//至此,getName方法的索引建立、獲取完畢
                    this.createInfo = null;
                }
            }
        }
    }

回到示例上層程式碼:methodProxy.invokeSuper(obj, args);這裡obj是代理物件,再看invokeSuper:

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();//建立和獲取索引
            FastClassInfo fci = fastClassInfo;
            //在obj即代理物件上呼叫索引為i2的方法
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

現在我們反編譯代理類的FastClass類看一下,fci.f2.invoke(fci.i2, obj, args);到底意味著什麼。
代理類FastClass(部分程式碼):

public class UserServiceB$$EnhancerByCGLIB$$a33459ad$$FastClassByCGLIB$$9e4fc4c5 extends FastClass {
    public UserServiceB$$EnhancerByCGLIB$$a33459ad$$FastClassByCGLIB$$9e4fc4c5(Class class_) {
        super(class_);
    }

    public int getIndex(Signature signature) {
        String string = signature.toString();
        switch (string.hashCode()) {
            case -2024387448 : {
            //在上面的Init方法中我們已經獲取了CGLIB$getName$0的索引,值為18
                if (!string.equals("CGLIB$getName$0(I)Ljava/lang/String;"))
                    break;
                return 18;
            }
            case 206620625 : {
                if (!string.equals("getName(I)Ljava/lang/String;"))
                    break;
                return 3;
            }
        }
        return -1;
    }
//在代理物件上呼叫索引為18的方法
    public Object invoke(int n, Object object, Object[] arrobject) throws InvocationTargetException {
        UserServiceB$$EnhancerByCGLIB$$a33459ad userServiceB$$EnhancerByCGLIB$$a33459ad = (UserServiceB$$EnhancerByCGLIB$$a33459ad) ((Object) object);
        try {
            switch (n) {
                case 3 : {
                    return userServiceB$$EnhancerByCGLIB$$a33459ad.getName(((Number) arrobject[0]).intValue());
                }
                //在代理物件上呼叫了CGLIB$getName$0方法,回到代理類原始碼,它呼叫super.getName,即目標類的真實邏輯
                case 18 : {
                    return userServiceB$$EnhancerByCGLIB$$a33459ad.CGLIB$getName$0(((Number) arrobject[0]).intValue());
                }
            }
        } catch (Throwable v1) {
            throw new InvocationTargetException(v1);
        }
        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
}

現在我們來回答一下為什麼示例程式碼中不能呼叫invoke,而是隻能呼叫invokeSuper。invoke程式碼:

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            //因為在回撥函式中obj傳入的代理物件,這裡實際上是在代理物件上呼叫
            //getName方法,將陷入無限遞迴,直至棧溢位
            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;
        }
    }

總結

型別 機制 回撥方式 適用場景 效率
JDK動態代理 委託機制,代理類和目標類都實現了同樣的介面,InvocationHandler持有目標類,代理類委託InvocationHandler去呼叫目標類的原始方法 反射 目標類是介面類 效率瓶頸在反射呼叫稍慢
CGLIB動態代理 繼承機制,代理類繼承了目標類並重寫了目標方法,通過回撥函式MethodInterceptor呼叫父類方法執行原始邏輯 通過FastClass方法索引呼叫 非介面類,非final類,非final方法 第一次呼叫因為要生成多個Class物件較JDK方式慢,多次呼叫因為有方法索引較反射方式快,如果方法過多switch case過多其效率還需測試