動態(tài)代理的實現(xiàn)與案例-cglib(轉(zhuǎn))

關(guān)于動態(tài)代理與其在應用中的實現(xiàn)參考了樓江航 的文章昌阿,有關(guān)ASM的未及研究,本文主要總結(jié)以下內(nèi)容:

動態(tài)代理的背景
動態(tài)代理的實現(xiàn)方式
JDK和CGLIB的實現(xiàn)方式
JDK和CGLIB的優(yōu)劣對比
應用中的使用場景

代理

代理的概念不言而喻纠俭,它可以實現(xiàn)過濾請求冰木、插入橫切邏輯等功能穷劈,應用場景豐富多彩。
代理的方式分為靜態(tài)代理和動態(tài)代理兩種片酝。

靜態(tài)代理

程序運行前代理類的字節(jié)碼文件依然存在囚衔,需要程序員編寫源文件。

缺點:要針對于每一個類撰寫代理類雕沿;對于單個被代理的類练湿,如果需要被代理的方法很多,又加大了工作量审轮。
優(yōu)點:直觀肥哎,可讀性較強。

動態(tài)代理

程序運行時動態(tài)生成代理類的字節(jié)碼文件疾渣,不需要程序員編寫代理類java文件篡诽。

缺點:由于是運行時動態(tài)生成的,因此可讀性不是很強榴捡;而且受限于被代理類自身的屬性(jdk需要提供接口杈女,cglib需要是非私有類)。
優(yōu)點:代碼更加簡潔吊圾,解放了無謂的編碼工作达椰。

實現(xiàn)方式

讓你來實現(xiàn)一個代理類,需要哪些上下文项乒,有哪些解決方案~正是JDK和CGLIB兩種解決方案的映射啰劲。
要生產(chǎn)一個類A的代理類,唯一需要了解的就是生成一個什么類檀何,因此就有了基于該類的接口構(gòu)造一個“A”蝇裤,或者繼承A生產(chǎn)一個“B”廷支。(一開始剛接觸jdk動態(tài)代理的時候我也很不解為什么要提供接口)。
至于如何生成一個class文件栓辜,在既定規(guī)則下你當然可以先生產(chǎn)java文件恋拍,再編譯成字節(jié)碼文件。而最好的做法是直接操作字節(jié)碼文件啃憎,jdk和cglib生成字節(jié)碼文件分別用的sun的ProxyGenerator和開源項目ASM字節(jié)碼框架芝囤。


環(huán)境準備

引cglib和asm的jar包

target類

接口:

public interface BookFacade {
    public void addBook();  
}

實現(xiàn):

public class BookFacadeImpl implements BookFacade, Serializable {
    private static final long serialVersionUID = 1L;
    @Override  
    public void addBook() {  
        System.out.println("增加圖書方法似炎。辛萍。。");  
    }  
}

代理類

JDK代理類

public class BookFacadeProxy implements InvocationHandler {  
    private Object target;  
    /** 
     * 綁定委托對象并返回一個代理類 
     * @param target 
     * @return 
     */  
    public Object bind(Object target) {  
        this.target = target;  
        //取得代理對象  
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
                target.getClass().getInterfaces(), this);   //要綁定接口(這是一個缺陷羡藐,cglib彌補了這一缺陷)  
    }  
    @Override  
    /** 
     * 調(diào)用方法 
     */  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {  
        Object result=null;  
        System.out.println("事物開始");  
        //執(zhí)行方法  
        result=method.invoke(target, args);  
        System.out.println("事物結(jié)束");  
        return result;  
    }  
}

CGLIB代理類

public class BookFacadeProxyCglib implements MethodInterceptor {
    private Object target;
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回調(diào)方法
        // enhancer.setCallbackType(this.getClass());
        enhancer.setCallback(this);
        // 創(chuàng)建代理對象
        return enhancer.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before run!");
        proxy.invokeSuper(obj, args);
        System.out.println("after run!");
        return null;
    }
}

client類

public class TestProxy {  
    private static String outputFile = "/Users/apple/Downloads/out";
    //控制cglib生成的class文件持久化到本地硬盤
    static {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, outputFile);
    }
    public static void main(String[] args) throws IOException {
        TestProxy testProxy = new TestProxy();
        BookFacadeImpl bookProxy = testProxy.cglibProxyClient();
        bookProxy.addBook();
        // jdkToFile(bookProxy);
        // testProxy.testSerail();
    }  
    public void testSerail() throws IOException {
        BookFacadeImpl obj = new BookFacadeImpl();
        toFile(obj);
    }
    public void jdkProxyClient() {
        BookFacadeProxy proxy = new BookFacadeProxy();  
        BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());  
        bookProxy.addBook();  
    }
    public BookFacadeImpl cglibProxyClient() {
        BookFacadeProxyCglib proxy = new BookFacadeProxyCglib();
        BookFacadeImpl bookProxy = (BookFacadeImpl) proxy.getInstance(new BookFacadeImpl());
        return bookProxy;
    }
    //JDK動態(tài)代理生成的字節(jié)碼文件持久化
    public static void jdkToFile(BookFacadeImpl obj) throws IOException {
        Class clazz = obj.getClass();
        String className = clazz.getName();
        byte[] classFile = ProxyGenerator.generateProxyClass(className, BookFacadeImpl.class.getInterfaces());
        FileOutputStream fos = new FileOutputStream(outputFile);
        // ClassReader cr = new ClassReader(className);
        // byte[] bits = cr.b;
        fos.write(classFile);
    }
    //另一種JDK字節(jié)碼文件持久化方法
    public static void toFile(BookFacadeImpl obj) throws IOException {
        Class clazz = obj.getClass();
        String className = clazz.getName();
        FileOutputStream fos = new FileOutputStream(outputFile);
        ClassReader cr = new ClassReader(className);
        byte[] bits = cr.b;
        fos.write(bits);
    }
}

JDK動態(tài)代理實現(xiàn)原理

反編譯后源文件

public final class $Proxy0 extends Proxy
    implements BookFacade, Serializable
{
    private static Method m1;
    private static Method m3;
    private static Method m0;
    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 ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    public final void addBook()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }
    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m3 = Class.forName("proxy.BookFacade").getMethod("addBook", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            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());
        }
    }
}

代理類生成原理

Proxy的newProxyInstance

生成代理類贩毕,然后把橫切邏輯作為構(gòu)造函數(shù)的入?yún)⑷嵗摯眍悺?/p>

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }
        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass(loader, interfaces);
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
    }

Proxy的getProxyClass

相當于CGLIB的Enhance.create,主要做的是key和緩存的管理仆嗦。

public static Class<?> getProxyClass(ClassLoader loader, 
                                         Class<?>... interfaces)
    throws IllegalArgumentException
    {
    // 如果目標類實現(xiàn)的接口數(shù)大于65535個則拋出異常(我XX辉阶,誰會寫這么NB的代碼啊瘩扼?)
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    //遍歷target類的接口
    Class proxyClass = null;
    String[] interfaceNames = new String[interfaces.length];
    Set interfaceSet = new HashSet();   // for detecting duplicates
    for (int i = 0; i < interfaces.length; i++) {
        String interfaceName = interfaces[i].getName();
        Class interfaceClass = null;
        try {
        interfaceClass = Class.forName(interfaceName, false, loader);
        } catch (ClassNotFoundException e) {
        }
        if (interfaceClass != interfaces[i]) {
        throw new IllegalArgumentException(
            interfaces[i] + " is not visible from class loader");
        }
        .......
    }
    // 把目標類實現(xiàn)的接口名稱作為緩存(Map)中的key谆甜,相當于CGLIB的KeyFactory
    Object key = Arrays.asList(interfaceNames);
    Map cache;
    synchronized (loaderToCache) {
        cache = (Map) loaderToCache.get(loader);
        if (cache == null) {
        cache = new HashMap();
        loaderToCache.put(loader, cache);
        }
    }
    synchronized (cache) {
        do {
        // 根據(jù)接口的名稱從緩存中獲取對象
        Object value = cache.get(key);
        if (value instanceof Reference) {
            proxyClass = (Class) ((Reference) value).get();
        }
        if (proxyClass != null) {
            // 如果代理對象的Class實例已經(jīng)存在,則直接返回
            return proxyClass;
        } else if (value == pendingGenerationMarker) {
            try {
            cache.wait();
            } catch (InterruptedException e) {
            }
            continue;
        } else {
            cache.put(key, pendingGenerationMarker);
            break;
        }
        } while (true);
    }
    try {
           .......
        // 動態(tài)生成代理對象
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces);
        try {
            // 根據(jù)代理類的字節(jié)碼生成代理類的實例
            proxyClass = defineClass0(loader, proxyName,
            proxyClassFile, 0, proxyClassFile.length);
        } catch (ClassFormatError e) {
            throw new IllegalArgumentException(e.toString());
        }
        }
        // add to set of all generated proxy classes, for isProxyClass
        proxyClasses.put(proxyClass, null);
    } 
    .......
    return proxyClass;
    }

ProxyGenerator的generateProxyClass

其實這里才是核心集绰,但是關(guān)于字節(jié)碼的處理就不做深究了规辱,倒不如去研究asm框架,性能差距可不小栽燕。

public static byte[] generateProxyClass(final String name,
                                            Class[] interfaces)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces);
        // 動態(tài)生成代理類的字節(jié)碼
        final byte[] classFile = gen.generateClassFile();
        // 持久化字節(jié)碼罕袋,client中的持久化也就是抄的這里
        if (saveGeneratedFiles) {
            java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        FileOutputStream file =
                            new FileOutputStream(dotToSlash(name) + ".class");
                        file.write(classFile);
                        file.close();
                        return null;
                    } catch (IOException e) {
                        throw new InternalError(
                            "I/O exception saving generated file: " + e);
                    }
                }
            });
        }
        return classFile;
    }

橫切邏輯的invoke邏輯

JDK的動態(tài)代理生成比較簡單。細心的話你會發(fā)現(xiàn)構(gòu)建代理類的時候入?yún)⒅挥薪涌诤蚦lassloader碍岔,因此JDK的動態(tài)代理類功能也比較單一浴讯。最后看下橫切邏輯是何時執(zhí)行的。
下面代碼摘自于代理類中反編譯后的addBook方法:
“h”即橫切邏輯類蔼啦,返回h中invoke實現(xiàn)榆纽,會發(fā)現(xiàn)依賴于源target實例,通過反射來調(diào)用target中相應的方法捏肢。而cglib在此也做了較大的優(yōu)化奈籽。

public final void addBook()
    {
        try
        {
            super.h.invoke(this, m3, null);
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

CGLIB動態(tài)代理實現(xiàn)原理

反編譯后的代理類


CGLIB反編譯后的代理類2

[圖片上傳失敗...(image-7de73e-1528599472132)]

代理類簡介

有個初步的認識,簡要介紹這9個標注的地方:

①回調(diào)函數(shù)猛计,由于是static類型唠摹,可以在class生成之后實例化前注入,適用于相同的class不同的回調(diào)函數(shù)的應用場景奉瘤,在有些screen中回調(diào)函數(shù)是帶有屬性的勾拉,因此不能生成class的時候就織入煮甥。
②用戶定義的回調(diào)函數(shù),織入class中藕赞,在諸多回調(diào)函數(shù)中擁有最高的優(yōu)先級成肘。
③持有代理類和target類;④持有代理類和target類中相應的方法,每個方法均含有兩個該組類斧蜕;⑤⑥代理類和target中對應方法的具體實現(xiàn)双霍。③~⑥都是用來替換反射調(diào)用的解決方法中的關(guān)鍵點,下面有詳述批销。
⑦非織入的回調(diào)函數(shù)處理邏輯洒闸,結(jié)合上面的描述可以總結(jié),回調(diào)函數(shù)的優(yōu)先級為:織入的回調(diào)函數(shù)>ThreadLocal回調(diào)函數(shù)>static回調(diào)函數(shù)均芽,結(jié)合織入的回調(diào)函數(shù)的filter機制可以構(gòu)建出更加強大的處理邏輯~
⑧橫切邏輯的執(zhí)行丘逸,由此可以看出,默認采用MethodProxy來實行“反射”調(diào)用掀宋,空間換時間深纲,加速了代理類的執(zhí)行速度。
⑨ThreadLocal或Static回調(diào)函數(shù)的注入,留給外界的接口,本文的案例也利用了該點功能夺颤。

代理類的生成流程


ClassGenerator類控制生成的流程,具體實現(xiàn)在Enhancer中币呵,并且Enhancer也提供了自定義的key。

KeyFactory

相比較上面JDK的key唆途,CGLIB中Key的生成方式比較獨特富雅,而生成Key的生成策略也是由Enhancer來決定,所以最終動態(tài)生成的類增加了一個EnhancerKeyFactory肛搬。

private static final EnhancerKey KEY_FACTORY =
      (EnhancerKey)KeyFactory.create(EnhancerKey.class);

ClassGenerator

抽象類没佑,控制class生成的流程,不提供具體的實現(xiàn)温赔。如下流程可以看到蛤奢,具體的實現(xiàn)都留給了子類Enhancer實現(xiàn)。

protected Object create(Object key) {
        try {
            Class gen = null;
            synchronized (source) {
                ClassLoader loader = getClassLoader();
                Map cache2 = null;
                cache2 = (Map)source.cache.get(loader);
                if (cache2 == null) {
                    cache2 = new HashMap();
                    cache2.put(NAME_KEY, new HashSet());
                    source.cache.put(loader, cache2);
                } else if (useCache) {
                    Reference ref = (Reference)cache2.get(key);
                    gen = (Class) (( ref == null ) ? null : ref.get()); 
                }
                if (gen == null) {
                    Object save = CURRENT.get();
                    CURRENT.set(this);
                    try {
                        this.key = key;
                        if (attemptLoad) {
                            try {
                                gen = loader.loadClass(getClassName());
                            } catch (ClassNotFoundException e) {
                                // ignore
                            }
                        }
                        if (gen == null) {
                            byte[] b = strategy.generate(this);
                            String className = ClassNameReader.getClassName(new ClassReader(b));
                            getClassNameCache(loader).add(className);
                            gen = ReflectUtils.defineClass(className, b, loader);
                        }
                        if (useCache) {
                            cache2.put(key, new WeakReference(gen));
                        }
                        return firstInstance(gen);
                    } finally {
                        CURRENT.set(save);
                    }
                }
            }
            return firstInstance(gen);
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw e;
        } catch (Exception e) {
            throw new CodeGenerationException(e);
        }
    }

GeneratorStrategy

正如其名陶贼,生成策略啤贩,提供給開發(fā)者擴展使用。
如下可以看到拜秧,類很簡潔痹屹,默認使用DebuggingClassWriter的字節(jié)碼處理策略,transform空實現(xiàn)枉氮,留做拓展志衍。

public class DefaultGeneratorStrategy implements GeneratorStrategy {
    public static final DefaultGeneratorStrategy INSTANCE = new DefaultGeneratorStrategy();
    public byte[] generate(ClassGenerator cg) throws Exception {
        ClassWriter cw = getClassWriter();
        transform(cg).generateClass(cw);
        return transform(cw.toByteArray());
    }
    protected ClassWriter getClassWriter() throws Exception {
      return new DebuggingClassWriter(ClassWriter.COMPUTE_MAXS);
    }
    protected byte[] transform(byte[] b) throws Exception {
        return b;
    }
    protected ClassGenerator transform(ClassGenerator cg) throws Exception {
        return cg;
    }
}

CallBackFilter

當類中每個方法所需要的攔截器都不盡相同的時候暖庄,CallBackFilter就派上用場了。

int index = filter.accept(actualMethod);
            if (index >= callbackTypes.length) {
                throw new IllegalArgumentException("Callback filter returned an index that is too large: " + index);
            }
            originalModifiers.put(method, new Integer((actualMethod != null) ? actualMethod.getModifiers() : method.getModifiers()));
            indexes.put(method, new Integer(index));
            List group = (List)groups.get(generators[index]);
            if (group == null) {
                groups.put(generators[index], group = new ArrayList(methods.size()));
            }
            group.add(method);

根據(jù)上面字節(jié)碼處理邏輯可以總結(jié)出:
(1)CallBackTypes可知楼肪,僅僅過濾織入class的那些回調(diào)函數(shù)
(2)根據(jù)CallBackFilter的Accept函數(shù)來確定代理方法所需要執(zhí)行的callBack下標培廓。
(3)一個方法只能織入一個CallBack

MultiCallBack

CallBack函數(shù)有多個子類,前面介紹的MethodInterCeptor只是其中一個春叫,再介紹一個延遲加載的攔截器肩钠,應用面也是比較廣的。
LazyLoader意為用到再加載暂殖,Bean的部分屬性設(shè)置為延遲加載在某些應用場景會有出乎意料的效果(案例)
LazyLoader的原理可以從反編譯的代碼中總結(jié)出來:

①分別是回調(diào)函數(shù)和緩存的結(jié)果
②每個方法的執(zhí)行都要被代理到回調(diào)方法中
③如果緩存中沒有价匠,則執(zhí)行LoadObject方法,并緩存結(jié)果央星。

FastClass

前言

如果說CGLIB優(yōu)于JDK的一點在于ASM框架的優(yōu)勢霞怀,那么另一個優(yōu)勢就是替換了原有了反射調(diào)用的局限性,對比下面JDK和CGLIB在invoke的異同:
CGLIB:


JDK:
JDK

分析上面兩段代碼可以看到莉给,CGLIB采用了Method的invokerSuper方法,走的并不是反射調(diào)用這一條道路廉沮。

FastClass實現(xiàn)原理

大致流程如下:

執(zhí)行invoke的時候cglib為代理類和taret類分別生成一份FastClass,主要用于Method名稱和方法執(zhí)行的映射
代理類中每個方法都有MethodProxy颓遏,此類封裝了代理類方法的上下文信息,用于和FastClass適配而已滞时。
代理類中的MethodProxy在執(zhí)行invokerSuper時會依據(jù)方法名調(diào)用到代理類的FastClass對應的方法去執(zhí)行叁幢。

FastClass的初始化

下面可以看到,初始化的時候會生成代理類和target類的FastClass

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

反編譯后的FastClass

public class BookFacadeImpl$EnhancerByCGLIB$1adc12da$FastClassByCGLIB$e7355050 extends FastClass
{
    public int getIndex(Signature signature)
    {
        String s = signature.toString();
        s;
        s.hashCode();
        JVM INSTR lookupswitch 27: default 527
    //                   -2055565910: 236
    //                   -1725733088: 247
    //                   -1696758078: 258
    //                   -1457535688: 269
    //                   -1411812934: 280
    //                   -1026001249: 291
    //                   -894172689: 302
    //                   -623122092: 312
    //                   -419626537: 323
    //                   243996900: 334
    //                   374345669: 345
    //                   560567118: 356
    //                   811063227: 367
    //                   946854621: 377
    //                   973717575: 388
    //                   1116248544: 399
    //                   1221173700: 410
    //                   1230699260: 420
    //                   1365077639: 431
    //                   1517819849: 442
    //                   1584330438: 453
    //                   1826985398: 464
    //                   1902039948: 474
    //                   1913648695: 485
    //                   1972855819: 495
    //                   1984935277: 506
    //                   2011844968: 516;
           goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L13 _L14 _L15 _L16 _L17 _L18 _L19 _L20 _L21 _L22 _L23 _L24 _L25 _L26 _L27 _L28
    //根據(jù)MethodProxy中的上下文信息適配合適的方法
    public Object invoke(int i, Object obj, Object aobj[])
        throws InvocationTargetException
    {
        //攔截器中傳入的代理類實例坪稽,依次為載體執(zhí)行相應的方法曼玩,如果執(zhí)行的是target類FastClass的話便會內(nèi)存泄露
        (BookFacadeImpl$EnhancerByCGLIB$1adc12da)obj;
        i;
        JVM INSTR tableswitch 0 26: default 392
    //                   0 128
    //                   1 143
    //                   2 147
    //                   3 159
    //                   4 169
    //                   5 191
    //                   6 201
    //                   7 206
    //                   8 226
    //                   9 237
    //                   10 248
    //                   11 259
    //                   12 272
    //                   13 276
    //                   14 286
    //                   15 291
    //                   16 296
    //                   17 301
    //                   18 316
    //                   19 320
    //                   20 332
    //                   21 336
    //                   22 341
    //                   23 355
    //                   24 378
    //                   25 382
    //                   26 387;
           goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12 _L13 _L14 _L15 _L16 _L17 _L18 _L19 
_L2:
        "CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V";
        equals();
        JVM INSTR ifeq 528;
           goto _L29 _L30

invoker vs invokerSuper

Method中執(zhí)行的是invokerSuper方法,因此會調(diào)用代理類的FastClass中相應方法窒百,進而回調(diào)代理類中的方法而進行target中原方法的調(diào)用黍判,還記得下面那段代碼么,上文提到過~篙梢,F(xiàn)astClass正是回調(diào)的該方法從而調(diào)用了target中的addBook實現(xiàn)顷帖。

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

有了上面的基礎(chǔ),來看看 invoker方法渤滞,該方法調(diào)用的是target類FastClass中相應的方法贬墩,試想如果你真的寫成了invoker方法,最后回調(diào)的可不是CGLIB$addBook$0這個方法了妄呕,而是代理類中的addBook方法了陶舞,那么循環(huán)調(diào)用就開始了,內(nèi)存會發(fā)生泄露绪励。
你或許你已經(jīng)發(fā)現(xiàn)了肿孵,CGLIB中很多field都是代理類和Target類雙份的论咏,其實target類感覺至此沒有用,還會有不可預料到的風險颁井。

CGLIB小結(jié)

動態(tài)生成了KEY厅贪、FASTCLASS、PROXYCLASS三種類型的CLASS
攔截器的優(yōu)先級:可織入>ThreadLocal>static
ASM框架處理字節(jié)碼+無反射調(diào)用改善了代理的效率
豐富的擴展點雅宾,延伸出來的功能點很多养涮,包括BeanMap、BeanCopy眉抬、延遲加載等相關(guān)功能贯吓。
Enhancer暴露出來的功能點有:

設(shè)置屬性
生成CLASS或者實例
對于已經(jīng)生成的CLASS反射調(diào)用注入相應的攔截器

案例分析

背景

在學習業(yè)務(wù)的時候發(fā)現(xiàn)了應用中關(guān)于異步加載的代碼,因此停下來研究了下蜀变,作者也沉淀了相關(guān)文檔:http://agapple.iteye.com/blog/918898悄谐。

異步并行加載

業(yè)務(wù)上下文

異步加載模板

線程池無阻塞處理異步調(diào)用的邏輯

CGLIB代理類的生成邏輯

模板是單例,因此緩存每個class库北,避免多余的開銷
局部變量不存在線程安全的問題爬舰,其實沒必要設(shè)置在ThreadLocal回調(diào)函數(shù)中,但是CGLIB只提供了兩種后處理方法寒瓦,這也是沒有辦法的事情情屹。
延遲加載攔截器,帶返回值的task杂腰,超時三秒垃你。

不得不說,線程+CGLIB+延遲加載=異步并行加載喂很,非常巧妙惜颇!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市少辣,隨后出現(xiàn)的幾起案子凌摄,更是在濱河造成了極大的恐慌,老刑警劉巖毒坛,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件望伦,死亡現(xiàn)場離奇詭異,居然都是意外死亡煎殷,警方通過查閱死者的電腦和手機屯伞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來豪直,“玉大人劣摇,你說我怎么就攤上這事」遥” “怎么了末融?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵钧惧,是天一觀的道長。 經(jīng)常有香客問我勾习,道長浓瞪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任巧婶,我火速辦了婚禮乾颁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艺栈。我一直安慰自己英岭,他們只是感情好,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布湿右。 她就那樣靜靜地躺著诅妹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毅人。 梳的紋絲不亂的頭發(fā)上吭狡,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天,我揣著相機與錄音堰塌,去河邊找鬼赵刑。 笑死,一個胖子當著我的面吹牛场刑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蚪战,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼牵现,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了邀桑?” 一聲冷哼從身側(cè)響起瞎疼,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎壁畸,沒想到半個月后贼急,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡捏萍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年太抓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片令杈。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡走敌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逗噩,到底是詐尸還是另有隱情掉丽,我是刑警寧澤跌榔,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站捶障,受9級特大地震影響僧须,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜项炼,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一担平、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芥挣,春花似錦驱闷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹋砚,卻和暖如春扼菠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坝咐。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工循榆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人墨坚。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓秧饮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親泽篮。 傳聞我的和親對象是個殘疾皇子盗尸,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容