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源码中也出现了该形式。

3.个人理解

当在DefaultNamespaceHandlerResolver初始化间打断点利用IDEA进行调试的时候,IDEA会自动开启一个线程调用该类的toString方法,在本例中就对handlerMappings进行了初始化,如果正常run的方式,是不会出现这种情况的。在toString方法中对handlerMappings进行初始化也可能为了缓存该数据,在下次需使用的时候,直接从内存中取值即可。

对于重写了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日,下午。

DefaultNamespaceHandlerResolver中handlerMappings如何初始化

上一篇:java基础知识回顾之---java String final类普通方法的应用之“两个字符串中最大相同的子串”


下一篇:Sublime Text2配置python环境