最近在debug dubbo代碼過程中遇到的很有趣的問題
我們都知道dubbo ReferenceBean是消費者的spring bean包裝,為了查一個consumer端的問題,在ReferenceBean的父類ReferenceConfig的 T get()方法(140行)打上了一個斷點。
當我debug 跟進init方法之后發現,ReferenceConfig的成員變量initialized(boolean類型),沒有初始化,值變成了true? 納尼。。。
在學習java的基礎知識的時候,我們就知道如果boolean類型如果不初始化變量,運行的時候默認初始為true。為什么變量initialized沒有賦值的情況,值是true呢?
更奇怪的是,debug模式下,因為initialized=true consumer的樁代碼(proxy)沒有生成,所以在業務層調用的時候跑出了NPE;但是在run模式下是對的。
初步懷疑
初步懷疑是debug模式和run模式下初始化的值是不同的,然后上google去搜索一下是不是存在這個問題,可惜沒有任何的收獲;同時在初始化的時候,直接把initialized初始化為false,防止缺省值初始成true。
但是問題依舊,放棄了這條思路!!!
然后開始懷疑是IDEA編輯器是不是在斷點處有setvalue的情況,就把IDEA全部緩存清空並重啟了(File->Invalidate....),debug后問題依然!!!
修改dubbo的源代碼加日志輸出
實在沒什么思路了,想着先去增加一些日志輸出,辦法雖然很土,但是可能會有線索。在initialized=true 這行代碼之后加一行輸出
System.out.println("init initialized="+initialized);
同時斷點還在ReferenceConfig.get()方法里面
當運行到斷點出,觀察console的輸出,果然有輸出!!!!
這說明當我斷點在ReferenceConfig.get()方法里時候,有線程執行了init()方法導致initialized的值被修改成了true(dubbo代碼只有一處修改ReferenceConfig.initialized值的地方,就在init里面)
所以只要找出哪個線程在我斷點的時候,執行了init方法就行了
日志輸出又立大功
繼續在init方法里面加日志輸出,以下日志能顯示出哪個堆棧調用了init方法
繼續debug運行,得到結果出乎意外
從日志輸出可以看出來,在ReferenceConfig構造函數的地方,調用了父類AbstractConfig.toString方法,而在AbstractConfig的toString方法里面反射間接調用了ReferenceConfig.init()方法
這下算是找到誰在我斷點的時候調用了ReferenceConfig.init()方法,就是toString()方法。
但是誰調用了toString()方法呢?
真想大白
首先可以排除程序在構造函數里面調用AbstractConfig.toString方法,還有一種可能就是IDEA 為了顯示觸發了這個類實例的toString方法。有了這個假設思路,寫了一個demo驗證一下
果不其然console里面輸出了invoke toString()
總結
IDEA 這類編輯器帶debug功能為了斷點處能顯示一個類的實例,就會反射調用實例的toString!!!!
真是一個有趣的發現!!!!
附送dubbo ReferenceConfig源碼鏈接