1. 程式人生 > >Cglib原始碼分析 invoke和invokeSuper的差別

Cglib原始碼分析 invoke和invokeSuper的差別

Cglib的例項

本文重在原始碼的分析,Cglib的使用不再複述。

//被代理類
public class InfoDemo {
    public void welcome (String person){
        System.out.println("welcome :" + person);
    }
}

public class CglibInfoProxy implements MethodInterceptor {
    private Object target;
    public Object newInstance(Object source){
        target = source;
        Enhancer enhancer = new
Enhancer(); enhancer.setSuperclass(this.target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("before method!!!"
); Object value = methodProxy.invokeSuper(o, objects); //Object value = methodProxy.invoke(o, objects); return value; } public static void main(String[] args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes"); InfoDemo instance = (InfoDemo) new
CglibInfoProxy().newInstance(new InfoDemo()); instance.welcome("zhangsan"); } }

代理類位元組碼分析

先來看main函式中new CglibInfoProxy().newInstance(new InfoDemo()),簡單來說這個方法是將infoDemo作為一個父類,通過asm位元組碼生成一個子類來繼承infoDemo。
InfoDemo$$EnhancerByCGLIB$$8b8da05b.class,就是生成的子類的位元組碼,稱這個子類為InfoDemo的代理類。擷取部分位元組碼顯示:

public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory {
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$welcome$0$Method;
    private static final MethodProxy CGLIB$welcome$0$Proxy;
    //..........................................省略
     static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("CglibTest.InfoDemo$$EnhancerByCGLIB$$8b8da05b");
        Class var1;
        CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("CglibTest.InfoDemo")).getDeclaredMethods())[0];
        CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
        //..........................................省略
     }

     final void CGLIB$welcome$0(String var1) {
        super.welcome(var1);
    }
    //先來判斷這個代理類中是否設定了方法攔截。如果設定了就呼叫該攔截器的intercept方法。
    //在本程式中,我們是設定了攔截器的。enhancer.setCallback(this);
    public final void welcome(String var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if(this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
        if(var10000 != null) {
            var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
        } else {
            super.welcome(var1);
        }
    }
    //..........................................省略
}

在這個代理類中,這裡有兩點需要注意。
1.它會將父類中的每一個方法,生成兩個與之對應。如父類中的welcome,在代理類中就會有CGLIBwelcome0,welcome的兩個方法與之對應。
2.每一個方法都會靜態塊中,經過MethodProxy.create生成對應的方法代理。如
MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");

當main函式執行到instance.welcome(“zhangsan”);這個語句時,會進入到代理類中的public final void welcome(String var1) 方法。進而執行了var10000.intercept()方法。
根據CglibInfoProxy中的intercept,先是會輸出一句“before method!!!”,然後呼叫methodProxy.invokeSuper(o, objects);
這裡的methodProxy對應的是var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)中的CGLIB$welcome$0$Proxy。

那麼就相當於執行CGLIB$welcome$0$Proxy.invokeSuper(o, objects),那麼來看看invokeSuper在做什麼。

MethodProxy 原始碼分析

public class MethodProxy {
    //下面的前三個變數在create方法中,都已經得到了初始值了。
    private Signature sig1;
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo;
    //FastClassInfo是在呼叫methodProxy.invoke或者methodProxy.invokeSuper中,init()會觸發,後面再來細看這個。
    private volatile MethodProxy.FastClassInfo fastClassInfo;

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
    public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if(this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    //本質就是要生成一個fastClassInfo,fastClassInfo裡面存放著兩個fastclass,f1,f2。
    //還有兩個方法索引的值i1,i2。
    private void init() {
        if(this.fastClassInfo == null) {
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                if(this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    //難道每一個方法,我們都去生成一個fastclass嗎?
                    //不是的,每一個方法的fastclass都是一樣的,只不過他們的i1,i2不一樣。如果快取中就取出,沒有就生成新的FastClass
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }
    }
//根據一個類的資訊,返回的該物件的一個Fastclass
    private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
        Generator g = new Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }
}

fci.f2.invoke(fci.i2, obj, args);fci存放了兩個類的fastClass。
其中f1是被代理的類對應的是InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class,
f2InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class
這也就是為什麼,在classes檔案中會生成三個class檔案了,一個代理類,兩個fastclass.
f2.invoke,那就說明會呼叫InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c這個類的方法。

代理類的FastClass

那再看看代理類的fastClass的位元組碼長啥樣
貼出InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c的部分位元組碼:

public class InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c extends FastClass {

        public InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c(Class var1) {
            super(var1);
        }
        public int getIndex(Signature var1) {
            String var10000 = var1.toString();
            switch(var10000.hashCode()) {
            case -2055565910:
                if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                    return 12;
                }
                break;
            case -1725733088:
                if(var10000.equals("getClass()Ljava/lang/Class;")) {
                    return 24;
                }
            case 1013143764:
                if(var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {
                    return 17;
                }
            }
            //----省略
        }

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

            try {
                switch(var10001) {
                case 0:
                    return var10000.toString();
                case 1:
                    return new Integer(var10000.hashCode());

                case 17:
                    var10000.CGLIB$welcome$0((String)var3[0]);
                }
                //----省略
            }
        }
    }

可以看到,會執行到voke方法中的
var10000.CGLIB$welcome$0((String)var3[0]);,而CGLIB$welcome$0其實就是直接呼叫了super.welcome(var1)的方法。輸出結束之後就會執行完畢。

那麼,如果現在呼叫的是methodProxy.invoke(o, objects);而不是invokeSuper會是怎麼樣的情況呢?
通過上面MethodProxy的原始碼,可以看到當執行invoke的時候會執行到fci.f1.invoke(fci.i1, obj, args);
即會呼叫被代理類的fastclass,InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class中的invoke。
開啟該class檔案我們會發現,執行的的是welcome()。

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
                switch(var10001) {
                case 0:
                    var10000.welcome((String)var3[0]);
                    return null;
                case 1:
                }
        }

那不就是和在main函式中instance.welcome(“zhangsan”)一樣的步驟了嘛,就會又開始迴圈呼叫,一直到棧溢位報錯。所以,invoke會造成OOM的問題。

相關推薦

Cglib原始碼分析 invokeinvokeSuper差別

Cglib的例項 本文重在原始碼的分析,Cglib的使用不再複述。 //被代理類 public class InfoDemo { public void welcome (String person){ System.out.prin

cglib invoke invokeSuper 可用的組合

html hello exc 場景 怎麽 java 總結 ML rgs 在深入字節碼理解invokeSuper無限循環的原因中,我們理解的cglib的原理和其中一個合理的調用方式。但是這個調用方式是基於類的,對所有實例生效。實際場景中,我們可能只是希望代理某個具體的實例,而

dubbo原始碼分析-Directory LoadBalance-筆記

Directory 訂閱節點的變化, 當zookeeper上指定節點發生變化以後,會通知到RegistryDirectory的notify方法 將url轉化為invoker物件 呼叫過程中invokers的使用 StaticDirectory: 靜態目

原始碼分析commitAllowingStateLoss() commit()的區別

之前在使用Fragment的時候偶爾會有這麼一個報錯,Can not perform this action after onSaveInstanceState,意思為無法再onSaveInstanceState之後執行該操作,這個操作就是指commit(),之前也沒怎麼在意

JDK原始碼分析—— ArrayBlockingQueue LinkedBlockingQueue

招賢納士: 我們叫數瀾 我們核心技術團隊來自阿里、華為、金蝶、移動、GE等 我們獲得來自阿里巴巴集團聯合創始人、湖畔山南總裁謝世煌、IDG合夥人牛奎光、洪泰等投資 我們的官網:https://www.dtwave.com 我們提供:期權、五險一金、試用期全薪、商業保險、免

Libevent原始碼分析-----日誌錯誤處理

日誌處理:         在Libevent的原始碼中,經常會見到形如event_warn、event_msgx、event_err之類的函式。這通常出現在程式碼中一些值是不合理時。這些函式就是Libevent的日誌函式。它能把這些不合理的情況打印出來,告知使用者

Zeppelin 原始碼分析-排程資源分析(2)

Scheduler 類 Scheduler 類是排程類的抽象類,其中定義了很多關鍵方法,比如 submit 方法等,並且是一個執行緒類,一直執行在主程序或者獨立 JVM 程序。 RemoteScheduler 類 RemoteScheduler

Java分散式跟蹤系統Zipkin(二):Brave原始碼分析-TracerSpan

Brave是Java版的Zipkin客戶端,它將收集的跟蹤資訊,以Span的形式上報給Zipkin系統。 (Zipkin是基於Google的一篇論文,名為Dapper,Dapper在荷蘭語裡是“勇敢的”的意思,這也是Brave的命名的原因) 我們一般

Android 之 三級快取(記憶體!!!、本地、網路)及記憶體LruCache擴充套件 及原始碼分析--- 學習程式碼講解

一. 三級快取簡介 如上圖所示,目前App中UI介面經常會涉及到圖片,特別是像“今日關注”新聞這類app中,圖片運用的機率十分頻繁。當手機上需要顯示大量圖片類似listView、gridView控制元件並且使用者會上下滑動,即將瀏覽過的圖片又載入一遍,

Libevent原始碼分析-timersignal處理

timer處理 evtimer_set(&ev, time_cb, NULL);//設定定時器事件 其中evtimer_set是個巨集定義 #define evtimer_set(ev, cb, arg) event_set

Java分散式跟蹤系統Zipkin(五):Brave原始碼分析-BraveSpringMVC整合

上一篇博文中,我們分析了Brave是如何在普通Web專案中使用的,這一篇博文我們繼續分析Brave和SpringMVC專案的整合方法及原理。 我們分兩個部分來介紹和SpringMVC的整合,及XML配置方式和Annotation註解方式 pom.xml新

深入原始碼分析TreeSetTreeMap

類似於前面介紹的HashMap和HashSet之間的關係,HashSet底層依賴於HashMap實現,而TreeSet底層則採用一個NavigableMap來儲存TreeSet集合的元素。但實際上,由於NavigableMap只是一個介面,因底層依然是使用Tre

spring註解原始碼分析-解析注入註解配置的資源

類內部的註解,如@Autowire、@Value、@Required、@Resource以及EJB和WebService相關的註解,是容器對Bean例項化和依賴注入時,通過容器中註冊的Bean後置處理處理這些註解的。 當使用Spring的註解功能時, <cont

Android 從原始碼分析BitmapBitmapFactory常用API

Bitmap recycle() 呼叫nativeRecycle()釋放該bitmap分配的native物件,清除對畫素資料的引用。不會同步的釋放畫素資料,只是簡單的允許bitmap在沒有其他引用時被垃圾回收。此bitmap被標記為dead,意味著呼叫get

spring 註解原始碼分析-掃描讀取bean定義

1.概述 從spring2.0以後的版本中,spring也引入了基於註解方式的配置,註解是jdk1.5中引入的一個新特性,用於簡化Bean的配置,某些場合可以取代xml配置檔案。 Spring IoC容器對於類級別的註解和類內部的註解分以下兩種策略: (1)類級別的註解

[原始碼分析]ArrayListLinkedList如何實現的?我看你還有機會!

> 文章已經收錄在 [Github.com/niumoo/JavaNotes](https://github.com/niumoo/JavaNotes) ,更有 Java 程式設計師所需要掌握的核心知識,歡迎Star和指教。 > 歡迎關注我的[公眾號](https://github.com/n

Tomcat詳解系列(3) - 原始碼分析準備分析入口

# Tomcat - 原始碼分析準備和分析入口 > 上文我們介紹了Tomcat的架構設計,接下來我們便可以下載原始碼以及尋找原始碼入口了。@pdai ## 原始碼下載和編譯 首先是去官網下載Tomcat的原始碼和二進位制安裝包,我這裡分析最新的[Tomcat9.0.39穩定版本](https://tomca

Spring原始碼分析總結(三)-JDK動態代理CGLIB代理以及攔截器

   一、JDK動態代理和CGLIB代理方式:      1、如果目標物件實現了介面,預設會採用JDK代理實現AOP,也可以通過配置強制使用CGLIB實現       2、如果目標物件沒有實現介面,必須採用CGLIB庫,Spring會自動在JDK方式和CGLIB方式之前轉

Mybatis原始碼分析(4)—— Mapper的建立獲取

Mybatis我們一般都是和Spring一起使用的,它們是怎麼融合到一起的,又各自發揮了什麼作用? 就拿這個Mapper來說,我們定義了一個介面,聲明瞭一個方法,然後對應的xml寫了這個sql語句, 它怎麼就執行成功了?這傢伙是怎麼實現的,帶著這個好奇心,我一步步跟蹤,慢慢揭開了它的

NSQ原始碼分析(四)——inFlightPqueuePriorityQueue優先順序佇列

   在Channel結構體中用到了兩種優先順序佇列pqueue.PriorityQueue和inFlightPqueue。 deferredMessages map[MessageID]*pqueue.Item deferredPQ pqueue.Priorit