1. 程式人生 > 其它 >老生常談系列之Aop--CGLIB動態代理的底層實現原理

老生常談系列之Aop--CGLIB動態代理的底層實現原理

老生常談系列之Aop--CGLIB動態代理的底層實現原理

前言

上一篇老生常談系列之Aop--JDK動態代理的底層實現原理簡單講解了JDK動態代理的實現,動態代理常用實現裡面的雙子星還有另一位--CGLIB,那麼這一篇就會介紹CGLIB動態代理。這篇文章還是複用之前老生常談系列之Aop--Spring Aop原理淺析文章的CGLIB部分的程式碼例子,CGLIB的使用是非常簡單的,只需要自己實現一個MethodInterceptor,然後使用Enhancer#create()方法就可以建立一個動態代理處理,然後通過生成的代理類呼叫方法,即可實現Aop的效果。是不是很好奇為什麼可以這麼簡單,接下來我們來分析CGLIB

幫我們做了什麼。

那麼接下來文章主要分為兩部分去解析

  • 動態代理的生成過程
  • 動態代理的呼叫過程

動態代理的生成

這一部分回答了動態代理是怎麼生成的,CGLIB底層幫我們做了什麼。可以看到建立代理物件離不開Enhancer類,那麼這個類的作用是什麼呢?摘取類上的註釋如下:

  Generates dynamic subclasses to enable method interception. This
  class started as a substitute for the standard Dynamic Proxy support
  included with JDK 1.3, but one that allowed the proxies to extend a
  concrete base class, in addition to implementing interfaces. The dynamically
  generated subclasses override the non-final methods of the superclass and
  have hooks which callback to user-defined interceptor
  implementations.

翻譯一下:通過生成動態子類讓方法攔截生效。此類開始是作為 JDK 1.3 中包含的標準動態代理支援的替代品,但它允許代理擴充套件具體的基類,除了實現介面。動態生成的子類覆蓋超類的非final方法,並具有回撥到使用者定義的攔截器實現的鉤子。

簡而言之,就是生成一個代理子類,呼叫方法的時候回撥到自定義實現的攔截器裡。

首先我們來看簡單的示例程式碼

    @Test
    public void cglibProxyTest(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CalculateServiceImpl.class);
        enhancer.setCallback(new MyMethodInterceptor());
        CalculateService calculateService  = (CalculateService) enhancer.create();
        calculateService.calculate();
    }

可以看到我們只需要設定superClasscallback後呼叫create()方法就可以生成一個想要的物件。接下來就看一下create()發生了什麼。

    /**
     * Generate a new class if necessary and uses the specified
     * callbacks (if any) to create a new object instance.
     * Uses the no-arg constructor of the superclass.
     * @return a new instance
     */
    public Object create() {
        classOnly = false;
        argumentTypes = null;
        return createHelper();
    }

可以看到這裡利用KEY_FACTORY生成一個key,這個key封裝了多個值,屬於multi-valued keys的實現。我們來看一下KeyFactory的用法。

    private Object createHelper() {
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

KeyFactory是類庫中重要的唯一標識生成器,用於CGLIB實現快取時的key,比較底層的基礎類。

摘取類上的註釋如下:

Generates classes to handle multi-valued keys, for use in things such as Maps and Sets.
To generate a <code>KeyFactory</code>, you need to supply an interface which
describes the structure of the key. The interface should have a
single method named <code>newInstance</code>, which returns an
<code>Object</code>. The arguments array can be
<i>anything</i>--Objects, primitive values, or single or
multi-dimension arrays of either. For example:
<p><pre>
    private interface IntStringKey {
        public Object newInstance(int i, String s);
    }
 </pre><p>
 Once you have made a <code>KeyFactory</code>, you generate a new key by calling
 the <code>newInstance</code> method defined by your interface.
 <p><pre>
     IntStringKey factory = (IntStringKey)KeyFactory.create(IntStringKey.class);
     Object key1 = factory.newInstance(4, "Hello");
     Object key2 = factory.newInstance(4, "World");
</pre><p>

翻譯一下:KeyFactory可以生成處理多值鍵的類,可以用於諸如 Maps 和 Sets 之類的東西。KeyFactory的使用也非常簡單,只需要提供一個介面,定義一個newInstance()方法,呼叫(IntStringKey)KeyFactory.create(IntStringKey.class)就可以生成一個key類。

接下來通過super.create(key)呼叫父類的create(key)方法。

    protected Object create(Object key) {
        try {
            ClassLoader loader = getClassLoader();
            Map<ClassLoader, ClassLoaderData> cache = CACHE;
            // 先嚐試通過快取獲取ClassLoaderData
            ClassLoaderData data = cache.get(loader);
            if (data == null) {
                synchronized (AbstractClassGenerator.class) {
                    cache = CACHE;
                    data = cache.get(loader);
                    if (data == null) {
                        Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
                        data = new ClassLoaderData(loader);
                        newCache.put(loader, data);
                        CACHE = newCache;
                    }
                }
            }
            this.key = key;
            // 這裡真正生成了代理類
            Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            return nextInstance(obj);
        } 
        // 省略異常
    }

進入ClassLoaderData#get()方法

        public Object get(AbstractClassGenerator gen, boolean useCache) {
            if (!useCache) {
              return gen.generate(ClassLoaderData.this);
            } else {
              Object cachedValue = generatedClasses.get(gen);
              return gen.unwrapCachedValue(cachedValue);
            }
        }

跟進generatedClasses.get(gen)方法,這裡第一次進來,前面都會為空,所以會進行節點建立createEntry()

    public V get(K key) {
        final KK cacheKey = keyMapper.apply(key);
        Object v = map.get(cacheKey);
        if (v != null && !(v instanceof FutureTask)) {
            return (V) v;
        }

        return createEntry(key, cacheKey, v);
    }

可以看到這裡使用了FutureTask去非同步執行建立,用於提升建立時候的效能。

    protected V createEntry(final K key, KK cacheKey, Object v) {
        FutureTask<V> task;
        boolean creator = false;
        if (v != null) {
            // Another thread is already loading an instance
            task = (FutureTask<V>) v;
        } else {
            // 建立一個FutureTask
            task = new FutureTask<V>(new Callable<V>() {
                public V call() throws Exception {
                    return loader.apply(key);
                }
            });
            // 校驗這個任務是否已經存在
            Object prevTask = map.putIfAbsent(cacheKey, task);
            if (prevTask == null) {
                // creator does the load
                // 執行FutureTask
                creator = true;
                task.run();
            } else if (prevTask instanceof FutureTask) {
                task = (FutureTask<V>) prevTask;
            } else {
                return (V) prevTask;
            }
        }

        V result;
        try {
            // 走到這裡說明是有正常執行的FutureTask,嘗試獲取FutureTask的結果
            result = task.get();
        } 
        // 省略部分異常
        if (creator) {
            map.put(cacheKey, result);
        }
        return result;
    }

上面的程式碼跟著註釋看一下,重點在這裡loader.apply(key),這個loadernew LoadingCache()的時候傳入的。

task = new FutureTask<V>(new Callable<V>() {
                public V call() throws Exception {
                    return loader.apply(key);
                }
            });

loader的邏輯如下:

            Function<AbstractClassGenerator, Object> load =
                    new Function<AbstractClassGenerator, Object>() {
                        public Object apply(AbstractClassGenerator gen) {
                            // 這裡生成代理class
                            Class klass = gen.generate(ClassLoaderData.this);
                            return gen.wrapCachedClass(klass);
                        }
                    };
            generatedClasses = new LoadingCache<AbstractClassGenerator, Object, Object>(GET_KEY, load);

跟進gen.generate(ClassLoaderData.this)方法

    protected Class generate(ClassLoaderData data) {
        Class gen;
        Object save = CURRENT.get();
        CURRENT.set(this);
        try {
            ClassLoader classLoader = data.getClassLoader();
            //省略部分邏輯和日誌,重點在這裡,這裡會生成代理類的位元組碼
            byte[] b = strategy.generate(this);
            String className = ClassNameReader.getClassName(new ClassReader(b));
            ProtectionDomain protectionDomain = getProtectionDomain();
            synchronized (classLoader) { // just in case
                if (protectionDomain == null) {
                    gen = ReflectUtils.defineClass(className, b, classLoader);
                } else {
                    gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
                }
            }
            return gen;
        }
        // 省略異常
    }

預設的實現是DefaultGeneratorStrategy,可以看到這裡先獲取了一個DebuggingClassWriter,CGLIB封裝ASM的處理類,用於生成class的byte流,通過GeneratorStrategy回撥ClassGenerator.generateClass(DebuggingClassWriter),將自定義的class物件的byte處理回撥給具體的CGLIB上層操作類,比如由具體的BeanCopier去控制位元組碼的生成。

    public byte[] generate(ClassGenerator cg) throws Exception {
        DebuggingClassWriter cw = getClassVisitor();
        transform(cg).generateClass(cw);
        return transform(cw.toByteArray());
    }

FastClassEmitter類的建構函式裡,通過上面傳入的DebuggingClassWriter封裝了ASM的相關操作,用於動態生成代理類的位元組碼,這裡不再深入ASM的原理,感興趣可以看位元組碼操作框架ASM的實現原理

        public void generateClass(ClassVisitor v) throws Exception {
            new FastClassEmitter(v, getClassName(), type);
        }

最後再通過transform(cw.toByteArray())得到一個byte[]陣列。好了,到這裡已經可以得到一個代理的位元組碼了。接下來回到AbstractClassGenerator#create()方法裡。

// obj為已經獲取到的代理類,這裡是一個Class物件
Object obj = data.get(this, getUseCache());
            if (obj instanceof Class) {
                return firstInstance((Class) obj);
            }
            return nextInstance(obj);

只需要把物件例項化返回,至此,已經獲取了一個代理類。

        protected Object firstInstance(Class type) {
            return ReflectUtils.newInstance(type,
                                            new Class[]{ Class.class },
                                            new Object[]{ this.type });
        }

動態代理的呼叫

程式碼樣例

這裡搞個例子HelloServiceHelloMethodInterceptor對它增強。

public class HelloService {
    public void sayHello(){
        System.out.println("hello");
    }
}

public class HelloMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("before say hello...");
        return methodProxy.invokeSuper(object,objects);
    }
}

測試方法,把生成的代理類存下來。

public class HelloTest {

    @Test
    public void cglibProxyTest(){
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"C:\\my_study_project");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HelloService.class);
        enhancer.setCallback(new HelloMethodInterceptor());
        HelloService helloService  = (HelloService) enhancer.create();
        helloService.sayHello();
    }

}

那麼上面的步驟生產了什麼呢?我們來看一下生成的類有哪些。

HelloService$$EnhancerByCGLIB$$91933e33是生成的代理類,HelloService$$FastClassByCGLIB$$a685f36d是目標類的FastClassHelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd是代理類的FastClass

反編譯後的程式碼

下面看一下每個類反編譯後的程式碼

代理類

每個類的程式碼都很長,這裡就不全部貼出來了,為了方便閱讀,這裡只留存sayHello()hashCode()方法做比對闡述。如果想檢視全部的程式碼,自己把測試程式碼跑一下就能在相應的路徑下找到這三個class檔案。

package io.codegitz.service;

public class HelloService$$EnhancerByCGLIB$$91933e33 extends HelloService implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    
    private static final Method CGLIB$sayHello$0$Method;
    private static final MethodProxy CGLIB$sayHello$0$Proxy;

    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;


    // 初始化該類的所有方法
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("io.codegitz.service.HelloService$$EnhancerByCGLIB$$91933e33");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        
        CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[]{"sayHello", "()V"}, (var1 = Class.forName("io.codegitz.service.HelloService")).getDeclaredMethods())[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create(var1, var0, "()V", "sayHello", "CGLIB$sayHello$0");
    }

    // 呼叫目標類方法
    final void CGLIB$sayHello$0() {
        super.sayHello();
    }
    // 代理邏輯的方法
    public final void sayHello() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
        } else {
            super.sayHello();
        }
    }

    //hashCode方法也類似
    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    // 生成MethodProxy,通過MethodProxy呼叫會生成fastClass,這是實現高效能呼叫的關鍵
    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case 1535311470:
            if (var10000.equals("sayHello()V")) {
                return CGLIB$sayHello$0$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$3$Proxy;
            }
        }
        return null;
    }
    // 初始化方法
    static {
        CGLIB$STATICHOOK1();
    }
}

代理類fastClass

fastClass為所有的方法都建立了索引,在呼叫的時候通過傳入索引來尋找方法,進而避免反射的效能開銷,這是一種典型的空間換時間實現。

package io.codegitz.service;

import io.codegitz.service.HelloService..EnhancerByCGLIB..91933e33;
import java.lang.reflect.InvocationTargetException;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.reflect.FastClass;

public class HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd extends FastClass {
    public HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd(Class var1) {
        super(var1);
    }

    // 通過方法簽名獲取方法索引
    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1411842725:
            if (var10000.equals("CGLIB$hashCode$3()I")) {
                return 16;
            }
            break;
        case 291273791:
            if (var10000.equals("CGLIB$sayHello$0()V")) {
                return 14;
            }
            break;
        case 1535311470:
            if (var10000.equals("sayHello()V")) {
                return 7;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 2;
            }
        }
        return -1;
    }

    // 通過方法名獲取索引
    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 0:
                    return 7;
                }
            }
            break;
        case -1983192202:
            if (var1.equals("CGLIB$sayHello$0")) {
                switch(var2.length) {
                case 0:
                    return 14;
                }
            }
            break;
        case -29025555:
            if (var1.equals("CGLIB$hashCode$3")) {
                switch(var2.length) {
                case 0:
                    return 16;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 2;
                }
            }
            break;
        }
        return -1;
    }

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

    // 通過傳入方法的索引var1獲取方法執行
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        91933e33 var10000 = (91933e33)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 2:
                return new Integer(var10000.hashCode());
            case 7:
                var10000.sayHello();
                return null;
            case 14:
                var10000.CGLIB$sayHello$0();
                return null;
            case 16:
                return new Integer(var10000.CGLIB$hashCode$3());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

    public Object newInstance(int var1, Object[] var2) throws InvocationTargetException {
        91933e33 var10000 = new 91933e33;
        91933e33 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 20;
    }
}

目標類fastClass

CGLIB不僅對代理類生成fastClass,會對原有的目標類也會生成一個fastClass,原理是類似的,都是通過建立方法的索引,通過傳入索引尋找到方法,執行方法,避免了反射獲取方法的效能開銷。

package io.codegitz.service;

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

public class HelloService$$FastClassByCGLIB$$a685f36d extends FastClass {
    public HelloService$$FastClassByCGLIB$$a685f36d(Class var1) {
        super(var1);
    }
    // 通過方法簽名獲取索引
    public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case 1535311470:
            if (var10000.equals("sayHello()V")) {
                return 0;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 3;
            }
        }
        return -1;
    }

    // 通過方法名獲取方法索引
    public int getIndex(String var1, Class[] var2) {
        switch(var1.hashCode()) {
        case -2012993625:
            if (var1.equals("sayHello")) {
                switch(var2.length) {
                case 0:
                    return 0;
                }
            }
            break;
        case 147696667:
            if (var1.equals("hashCode")) {
                switch(var2.length) {
                case 0:
                    return 3;
                }
            }
        }
        return -1;
    }

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

    // 根據傳入的var1獲取對應的方法執行
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        HelloService var10000 = (HelloService)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.sayHello();
                return null;
            case 3:
                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 {
        HelloService var10000 = new HelloService;
        HelloService 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 3;
    }
}

呼叫過程分析

這些碼看起來是不是很亂?完全不知道從哪裡開始執行?

在開始程式碼分析之前,先看一下執行流程圖,步驟還是比較簡單明瞭

接著下一步,這裡就是進入動態代理類的邏輯,可以看HelloService$$EnhancerByCGLIB$$91933e33#sayHello()方法。

    public final void sayHello() {
        // 獲取攔截器,這裡就是HelloMethodInterceptor
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        // 不為空,則執行攔截器
        if (var10000 != null) {
            // 注意這裡傳入的引數,這裡傳入了一個method和一個MethodProxy,這裡就會進入到自定義的HelloMethodInterceptor裡面的邏輯
            var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
        } else {
            super.sayHello();
        }
    }

這裡是通過methodProxy.invokeSuper(object,objects),呼叫invokeSuper()方法,注意這裡methodProxy還有個invoke()方法可以呼叫,那麼這兩者有什麼區別呢?顯而易見invokeSuper()就是呼叫父類的方法,而invoke()是呼叫代理經過攔截器的方法,如果呼叫invoke()那麼每次都會走到攔截器,會造成死迴圈。

跟進methodProxy.invokeSuper()方法,根據註釋可以看到,這裡就是呼叫了原有的沒有經過代理的方法。

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

可以看到,這裡的呼叫跟反射呼叫是有區別的。反射呼叫一般是直接把方法傳入,然後直接invoke(),而這裡會先進行init(),初始化一個FastClassInfo,再通過fci.f2.invoke(fci.i2, obj, args)去呼叫方法,這裡就是前面說的實現高效能呼叫的關鍵,這裡會為代理類方法和實現類的FastClass,然後在呼叫時通過傳入方法的下標索引直接獲取方法執行,從而實現了空間換時間操作。

來看init()方法,這個方法是用來初始化fastClassInfo類的,詳細的初始化過程就不解析了,這裡只是最終生成了什麼就好。

    private void init()
    {
        /* 
         * 使用 volatile 不變數允許我們以原子方式初始化 FastClass 和方法索引對
         * Using a volatile invariant allows us to initialize the FastClass and
         * method index pairs atomically.
         * 
         * 雙重檢查鎖定在 Java 5 中使用 volatile 是安全的。在 1.5 之前,此程式碼可能允許多次例項化 fastClassInfo,這似乎是良性的。
         * 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();
                    // 生成目標類fastClass
                    fci.f1 = helper(ci, ci.c1);
                    // 生成代理類fastClass
                    fci.f2 = helper(ci, ci.c2);
                    // 生成目標類index
                    fci.i1 = fci.f1.getIndex(sig1);
                    // 生成代理類index
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

以下是初始化時各個屬性的賦值

初始化完成後,就可以回到fci.f2.invoke(fci.i2, obj, args)呼叫上了,可以看到fci.f2的型別是HelloService$$EnhancerByCGLIB$$91933e33$$FastClassByCGLIB$$33d595dd,也就是上面貼出來的代理類的fastClass,來看一下這個類的invoke()方法

檢視反編譯的invoke()方法程式碼

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

        try {
            switch(var10001) {
            case 2:
                return new Integer(var10000.hashCode());
            case 7:
                var10000.sayHello();
                return null;
            case 16:
                var10000.CGLIB$sayHello$0();
                return null;
            case 17:
                return new Integer(var10000.CGLIB$hashCode$3());
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

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

這裡switch會匹配到16,然後執行var10000.CGLIB$sayHello$0(),這個var10000的型別是

找到HelloService$$EnhancerByCGLIB$$91933e33#CGLIB$sayHello$0()方法,可以看到,這裡直接呼叫了HelloService#sayHello()方法。同樣,methodProxy#invoke()方法邏輯也是類似,注意區分呼叫的時候不要死迴圈就是了。

    final void CGLIB$sayHello$0() {
        super.sayHello();
    }

到這裡,可以看到CGLIB生成的代理方法呼叫時,先經過呼叫攔截器,然後再呼叫到目標方法,其中methodProxy呼叫目標方法時,會生成fastClassfastClass中存有代理類和目標類的所有方法以及匹配的下標,通過傳入的下標就可以尋找到對應的方法,這裡的方法呼叫只需要第一次進來初始化fastClass,後續可以直接呼叫,從而提高執行的效能,這也是CGLIB執行效率比JDK動態代理高的關鍵。這裡空間換時間的思想值得我們借鑑,適當地消耗記憶體來提升執行效率是完全值得的。

總結

回顧一下這篇文章,前半部分通過一個例子,大概講解了CGLIB生成一個代理類的步驟,但是具體整合ASM部分的位元組碼操作被略過,水平有效,不敢造次。挖了個坑,以後有能力再填。後半部分結合反編譯的class檔案,解釋了呼叫的過程,這部分很簡單,自己除錯一下應該很快就能理清。

如果有人看到這裡,那在這裡老話重提。與君共勉,路漫漫其修遠兮,吾將上下而求索。