DefaultNamespaceHandlerResolver中handlerMappings如何初始化

前言:最近一直在看Spring源码,今天在调试的时候发现一个小问题:在注册bean时,需要初始化Spring默认命名空间处理器,具体在DefaultNamespaceHandlerResolver中实现,但是当Debug时,发现handlerMappings已经赋值,顿感奇怪。下面记录一下该问题。


1.调用入口

Spring的代码调用链非常的庞大,因此阅读源码的时候,也非常耗时,这里给出创建DefaultNamespaceHandlerResolver的调用入口。

2.Debug时出现的现象

在上图537行处打一断点,运行后结果如下:

注意this对象中抛出了异常,此时handlerMappings还为null,this中抛出的异常信息如下:

此异常说明在Debug的时候,调用了toString()方法,但此时DefaultNamespaceHandlerResolver还未初始化完,所以抛出异常。猜测为IDEA另起了一个线程调用了toStirng()方法。继续调试代码。

此时断点在构造函数括号处,程序还未执行完,此时this对象处未抛异常了,此时handlerMappings还为null。查看this中的信息。

生成空间处理器的键值对。继续调试程序,退出DefaultNamespaceHandlerResolver构造函数。

此时handlerMappings已经有9个值了。说明对其进行了初始化,根据上面的调试信息,看DefaultNamespaceHandlerResolver的toString()方法。

可见在toString()方法中调用了getHandlerMappings方法。

注:该代码是不是很熟悉,使用了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结果如下:

如果不设置断点,调试结果如下:

总结

在调试Spring源码的时候,最开始出现改问题时,觉得很不可思议,后面通过不断的调试,猜测出该结论,同时觉得Spring真的非常强大,还需继续努力,已经看了一段时间了,后面慢慢整理出来,加强印象与理解。



by Shawn Chen,2018.11.22日,下午。

原文地址:https://www.cnblogs.com/morewindows0/p/10002492.html

时间: 2024-10-11 00:21:28

DefaultNamespaceHandlerResolver中handlerMappings如何初始化的相关文章

springmvc中配置servlet初始化类

<bean  id="InitStart" lazy-init="false" init-method="InitSystem" class="my.spring.uitl.InitStart"></bean> 配置在springmvc的配置文件中 只要项目启动,就会默认执行这个类的这个方法 相比静态类代码块的好处, 有点在tomcat启动时就会调用如果有错立即报错,静态代码块,调用时才会报错 作用 可

java中的静态初始化块

Java 中可以通过初始化块进行数据赋值.如: 在类的声明中,可以包含多个初始化块,当创建类的实例时,就会依次执行这些代码块.如果使用 static 修饰初始化块,就称为静态初始化块. 需要特别注意:静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量. 我们来看一段代码: 运行结果: 通过输出结果,我们可以看到,程序运行时静态初始化块最先被执行,然后执行普通初始化块,最后才执行构造方法.由于静态初始化块只在类加载时执行一次,所以当再次创建对

关于Quartus和ISE中ROM的初始化和仿真的一些小结

最近在玩Altera的FPGA,当我用Quartus II自带的IP核生成ROM时,出现了各种问题,于是在网上各种查资料,终于解决了我的问题.这里做一下小结,方便自己日后查阅. Quartus II 和ISE在仿真和初始化时有些些区别,这里简要介绍一下二者的初始化和仿真步骤:1.用Quartus II创建并仿真ROM Step1:在Quatus II工程下生成一个ROM Step2:编写.mif文件,作为ROM的初始化文件 Step3:将.mif文件拷贝到Modelsim工程下 Step4:进行

java类中各成员初始化的顺序

了解java中类各个成员的初始化顺序是非常重要的,这样你可以对类有全局的认识.不说太多,直接看下面的例子 class Father { static{ System. out.println("父类静态代码块初始化" ); } { System. out.println("父类代码块初始化" ); } private static String s=print(); public static String print() { System. out.println

vb中数组的初始化

vb中数组的初始化 问题: 计划使用数组存储以下值: "零分,班序,年名,序,组名,级名,总分3,总分5,总分9,总序,语序,数序,英序,物序,化序,政序,历序,地序,生序" 方法: 在vb中数组初始化比较麻烦 1.标准做法:     dim field(19) as string '需要手工计算下标上限     fiels(0)="零分" '逐行赋值     field(1)="班序"     ...     field(18)="生

Python中,如何初始化不同的变量类型为空值

参考文章  Python中,如何初始化不同的变量类型为空值 常见的数字,字符,很简单,不多解释. 列表List的其值是[x,y,z]的形式 字典Dictionary的值是{x:a, y:b, z:c}的形式 元组Tuple的值是(a,b,c)的形式 所以,这些数据类型的变量,初始化为空值分别是: 数值 digital_value = 0 字符串 str_value = "" 或 str_value = ” 列表 list_value = [] 字典 ditc_value = {} 元组

Java中数组的初始化方式

Java中数组的初始化方式    初始化方式有两种: 1.静态初始化:初始化时由程序猿显式指定每一个数组元素的初始值,由系统指定数组长度 2.动态初始化:初始化时由程序猿仅仅指定数组长度,由系统为数组元素分配初始值

Nginx 中 HTTP模块初始化

概述 在前面的文章< Nginx 配置解析>简单讲解了通用模块的配置项解析,并且大概讲解了HTTP 模块的配置项解析过程,本文更具体的分析 HTTP 模块的初始化过程.HTTP 模块初始化过程主要有:上下文结构初始化.配置项解析.配置项合并.server 相关端口设置. HTTP 模块接口 ngx_http_module_t 结构体 在 Nginx 中,结构体 ngx_module_t 是 Nginx 模块最基本的接口.对于每一种不同类型的模块,都有一个具体的结构体来描述这一类模块的通用接口.

再次认识Java中构造器的初始化与继承

class Insect { private int i = 9; protected static int j; Insect() { Print.print("i = " + i + ",j = " + j); j = 39; } private static int x1 = printInit("static Insect.x1 初始化"); static int printInit(String s) { Print.print(s);