天天看點

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

前言:最近一直在看Spring源碼,今天在調試的時候發現一個小問題:在注冊bean時,需要初始化spring預設命名空間處理器,具體在DefaultNamespaceHandlerResolver中實作,但是當Debug時,發現handlerMappings已經指派,頓感奇怪。通過調試發現了該問題産生的原因,遂記錄下來。

1.調用入口

spring的代碼調用鍊非常的龐大,是以閱讀源碼的時候,也非常耗時,這裡給出建立DefaultNamespaceHandlerResolver的調用入口。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化
DefaultNamespaceHandlerResolver中handlerMappings如何初始化

2.Debug時出現的現象

在上圖537行處打一斷點,運作後結果如下:

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

注意this對象中抛出了異常,此時handlerMappings還為null,this中抛出的異常資訊如下:

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

此異常說明在Debug的時候,調用了toString()方法,但此時DefaultNamespaceHandlerResolver還未初始化完,是以抛出異常。猜測為IDEA另起了一個線程調用了toStirng()方法。繼續調試代碼。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

此時斷點在構造函數括号處,程式還未執行完,此時this對象處未抛異常了,handlerMappings還為null。檢視this中的資訊。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

生成空間處理器的鍵值對。繼續調試程式,退出DefaultNamespaceHandlerResolver構造函數。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

此時handlerMappings已經有9個值了,說明對其進行了初始化。根據上面的調試資訊,檢視DefaultNamespaceHandlerResolver的toString()方法。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

可見在toString()方法中調用了getHandlerMappings方法。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

注:該代碼是不是很熟悉,使用了Double-Check的方式避免非線程安全問題,為單例模式的一種實作形式,是不是很神奇,spring源碼中應用了Double-Check。

3.個人了解

當在DefaultNamespaceHandlerResolver初始化過程中打斷點并利用IDEA進行調試的時候,IDEA會自動開啟一個線程調用該類的toString方法,在本例中就對handlerMappings進行了初始化;如果正常run的方式運作,是不會出現這種情況的。

對于重寫了toString方法的類,在用Debug調試時會出現上述的情況,可寫簡單代碼進行驗證,具體代碼如下:

1 public class ToStringTest {
 2     /**
 3      * 驗證Debug時,idea會開啟一個線程調用對象的toString方法
 4      */
 5     public static void main(String[] args) {
 6 
 7         WilltoStringInvoked will = new WilltoStringInvoked();
 8 
 9         System.out.println("如果在這裡設定斷點,則輸出1");
10 
11         System.out.println(will.getValue());
12 
13         System.out.println("如果不設定斷點,則輸出0");
14 
15     }
16 
17     static class WilltoStringInvoked {
18         private volatile int value = 0;
19 
20         private int setValue() {
21             if (value == 0) {
22                 synchronized (this) {
23                     if (value == 0) {
24                         value = 1;
25                     }
26                 }
27             }
28             return value;
29         }
30 
31         public int getValue() {
32             return value;
33         }
34 
35         @Override
36         public String toString() {
37             return "This value is:" + setValue();
38         }
39     }
40 }      

在第9行處設定斷點,Debug結果如下:

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

如果不設定斷點,調試結果如下:

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

總結

在調試spring源碼的時候,最開始出現該問題覺時覺得很不可思議,後面通過不斷的調試,猜測出該結論,并進行驗證;同時覺得spring真的非常強大,還需繼續努力,已經看了一段時間了,後面慢慢整理出來,加強印象與了解。

by Shawn Chen,2018.11.22日,下午。

=========================================================

比你優秀的人比你還努力,你有什麼資格不去奮鬥!

__一個有理想的程式員。

繼續閱讀