SpringtMVC中配置 <mvc:annotation-driven/> 与 <mvc:default-servlet-handler/> 源码解析
上一篇有提到,当有、无这两个标签时,SpringtMVC 底层所采用的 HandlerMapping 以及 HandlerAdapter 是不一样的。现在就来进行源码调试,揭开 SpringtMVC 底层实现的庐山真面目。
Demo代码地址:
https://github.com/cyhbyw/springMVC_atguigu_TongGang
工程名称:
springMVC_DebugSourceCode
现在开始调试。
==============>>>>
PS:图片可能不是很清晰,可以右击图片、选择在新标签页中查看
或者,可以右击图片,选择“图片另存为”保存在本地并编好号(建议直接以01、02、03……来编号)
或者,可以右击图片,选择“复制图片”,再保存到本地并编好号(建议直接以01、02、03……来编号)
以上三种办法,任意选择喜欢的一种,以获得并查看更清晰的图片~~
<<<<==============
先来看看没有这两个标签的情况下,SpringMVC的行为吧。提示:需要在XML配置文件中注释掉这两个标签的内容!
01. Bean定义文档的阅读器(其实就是读取XML配置文件中的内容);可以看到,SpringMVC 会读取到 component-scan 的元素,然后调用Line166的方法;注意线程堆栈信息,此方法位于 AbstractApplicationContext 的 Line501 的 refresh() 方法调用路径中(refresh() 方法是SpringIOC容器管理的核心方法,后面会再提及)
02. 解析元素;先调用 Line1339 的方法,再调用 Line1343 的方法;如图,Line1345 的 resolver() 方法很重要(后面详解)
03. 现在进入 resolve() 方法;然后调用 Line128 的 init() 方法
04. init() 方法就是注册一批Bean定义解析器;比如 Line35 就定义了 component-scan 元素的解析器
05. 注册的方式就是放入到 parsers 这个 map 中(这里放入,后面就会取出来使用)
06. 回到上一层,resolve() 方法处理完成后,调用 handler.parse() 方法
07. parse() 方法会先去找对应元素的 parser
08. 找的本质其实就是从刚才的 map 中取出对应元素的值
09. 找到 parser 后,继续调用 parse() 方法
10. 继续调用
11. 继续调用
12. 下面两张图片中断点所在行的 beanDefs.add(registerPostProcessor 方法的 registerPostProcessor() 方法会依次被调用(可以理解,或者说,等价于 for 循环调用,只是 SpringtMVC 还有其它的判断逻辑)
13. 继续调用 registerBeanDefinition() 方法
14. 通过 Line851 行将 beanName 添加到 beanDefinitionNames 中,以方便后续使用
15. 多次调用后,可以看到,beanDefinitionNames 中的元素数量在增加
16. XML 配置文件中定义的 component-scan 元素已经处理完成,现在继续处理 bean 元素(这是的 bean 元素其实是 XML 文件中定义的 InternalResourceViewResolver,只是没有为它的 id/name 赋值,所以下图中显示为 "bean: null")
17. 同理,InternalResourceViewResolver 会被添加到 beanDefinitionNames 中
18. 到目前为止,beanDefinitionNames 已经存入8个元素,是时候想想,什么时候会使用 beanDefinitionNames 了。
前面也提及过,AbstractApplicationContext 中有个 refresh() 方法,此方法是 SpringIOC 容器管理的核心方法,里面包含了对其它几个重要方法调用。
refresh() 方法非常重要!!!!
refresh() 方法非常重要!!!!
refresh() 方法非常重要!!!!
上面1--17步中方法的调用,其实都全部位于此处 Line501 的 obtainFreshBeanFactory() 方法中。现在,是时候转向其它方法的调用了。
调试发现,接下来 SpringtMVC 会走到 Line532 的 finishFresh() 方法。
19. 现在到达 DispatcherServlet 的 initStrategies() 方法;注意查看下图中的堆栈路径,有点多,不再详述了;同时,可以看到,此处 Line482 的 initHandlerMappings(context) & Line483 的 initHandlerAdapters(context) 方法其实就是从 context-->beanFactory-->beanDefinitionNames 中去获取 HandlerMapping & HandlerAdapter
20. 不好意思,获取不到(这个很容易理解,因为上面我们看到的 beanDefinitionNames 确定没有任何一个 HandlerMapping 或者 HandlerAdapter),于是代码转到 Line588 去获取默认值
21. 默认值是从 defaultStrategies 中获取
22. defaultStrategies 其实是采用 static{} 去读取 DispatcherServlet.properties 文件中的内容
23. 文件内容如下;默认两个 HandlerMapping 以及三个 HandlerAdapter (这就是没有配置这两个标签时 SpringtMVC 所采用的默认值)
24. 初始化 HandlerAdapter 的逻辑类似,不再详述
以上24步,是没有添加这两个标签时 SpringtMVC 的处理流程。接下来分析一下有这两个标签时的处理流程。
总体来说,大同小异,只是中间有个地方创建了不同的实例、然后调用了不用实例的方法、进而得到不同的Bean。
来具体看看吧。相同的逻辑不再赘述,下面的说明及配图主要体现在一些不同的点上。
01. 现在开始处理 annotation-driven 元素
02. 得到一个不同的 NamespaceHandler 并调用它的 init() 方法
03. 注册内容当然不一样;从图中可以明显看到,第一个就是 annotation-drivern 且对应的类是 AnnotationDrivenBeanDefinitionParser
04. 获取的时候,当然是拿到刚才注册进去的 AnnotationDrivenBeanDefinitionParser
05. parse() 方法自然也不一样;注意 Line204 行会注册 HANDLER_MAPPING_BEAN_NAME
06. 而这个 HANDLER_MAPPING_BEAN_NAME 其实就是 RequestMappingHandlerMapping
07. 可以看到 RequestMappingHandlerMapping 已经被成功地添加到 beanDefinitionNames 中(那么,后续使用时,就可以取出它)
08. 注册 HANDLER_ADAPTER_BEAN_NAME
09. HANDLER_ADAPTER_BEAN_NAME 其实就是 RequestMappingHandlerAdapter
10. 将 RequestMappingHandlerAdapter 添加到 beanDefinitionNames 中(之后就可以使用啦~~)
简单总结起来就是:
1. 解析 annotation-driven 元素/标签
2. 根据 annotation-driven 创建 AnnotationDrivenBeanDefinitionParser 对象
3. AnnotationDrivenBeanDefinitionParser 中注册 RequestMappingHandlerMapping & RequestMappingHandlerAdapter 这两个 Bean
4. DispatcherServlet 中的 initStrategies 方法会分别调用 initHandlerMappings & initHandlerAdapters 方法从已经注册的 beanDefinitionNames 中初始化 HandlerMapping & HandlerAdapter
5. DispatcherServlet 中的 doDispatch 方法使用 HandlerMapping & HandlerAdapter