SpringMVC中的handlerMappings对象用法
作者:喜欢火影的木易杨 发布时间:2023-01-09 21:38:36
从SpringMVC源码解析所用的例子,一个http://localhost:9090/web/hi?name=yang请求调用到下面的地方,发现一个特殊的对象handlerMappings,通过请求uri去找handler的时候需要通过这个handlerMapping来找,那么handlerMappings是怎么样的呢?
从断点调试中,我们可以看到这handlerMappings一共有5个,分别是WebMvcConfigurationSupportXXX,RequestMappingHandlerMapping和BeanNameUrlHandlerMapping。
首先,我们先了解一下这个handlerMappings是什么?从官方描述看,handlerMappings是DispatcherServlet内部的一种Bean类型,DispatcherServlet内部有很多Bean类型,包括HandlerMapping,HandlerAdapter,HandlerExceptionResolver,ViewResolver,LocaleResolver ,ThemesResolver,Multipart Resolver和FlashMapManager 。
其中HandlerMapping是一个将请求与用于预处理和后处理的 * 列表一起映射起来的处理程序,除了WebMvcConfigurationSupportXXX供用户自定义的映射处理器之外,
主要有以下两种内置类型:
(1)RequestMappingHandlerMapping(它支持@RequestMapping修饰的方法)
(2)SimpleUrlHandlerMapping(它为处理程序维护URI路径模式的显式注册)
一.handlerMappings集合内部的元素是什么,有什么作用?
通过官方文档描述,handlerMappings集合内部最主要有两种映射处理器类型RequestMappingHandlerMapping和SimpleUrlHandlerMapping,它们分别用于处理不同类型的Controller。
我们知道可以通过注解@Controller或@RestController注解方式来声明一个Controller,然后使用@RequestMapping来修饰类名或方法名,这种方式其实是由RequestMappingHandlerMapping映射处理器来对请求地址和方法名进行映射的;那么SimpleUrlHandlerMapping又用于处理哪种方式的Controller呢,那就是采用实现Controller接口或实现HttpRequestHandler接口的类,这种声明Controller的方式是早期的做法现在在实际开发中用的比较少了但是也是SpringMVC所支持的。
对于RequestMappingHandlerMapping
这种类型的handlerMapping是我们通过@RequestMapping+请求地址来修饰方法时,会由这么一个映射器来对这种配置方式进行解析和映射处理,也就是说RequestMappingHandlerMapping类型的映射器是为注解声明方式的映射专门设计的,<K,V>映射关系会存储在这个handlerMapping的Map中。
对于SimpleUrlHandlerMapping
这个映射器则是为继承Controller类或实现HttpRequestHandler接口的类进行解析和映射处理的。
用代码举例子来验证,我们分别新建两个类分别实现Controller接口和实现HttpRequestHandler接口,在类中写请求接收方法,那么通过这两种声明的Controller通过请求地址在查询请求方法时就会由SimpleUrlHandlerMapping进行存储和映射,<K,V>映射关系会存储在这个SimpleUrlHandlerMapping的Map中。
(1)实现Controller接口的方式:
(2)实现HttpRequestHandler接口的方式:
然后重新进行debug,使用http://localhost:9090/beanweb或http://localhost:9090/beanweb2请求,就会看到在handlerMapping中查找handler时会跳过第一种注解类型的RequestMappingHandlerMapping,而真正进行解析处理的就是第二种非注解方式SimpleUrlHandlerMapping:
因此我们现在就明白了这个handlerMappings映射处理器集合的数据是怎么样的,集合内存储的映射处理器分别处理怎么样的请求。那么这个集合数据是怎么初始化的呢?
二.handlerMappings集合是怎么初始化的?
通过IDEAJ的Find Usages工具,在本文图一的this.handlerMappings进行调用搜索,可以一层层找到如下调用链:
HttpServletBean.init-->initServletBean()-->FrameworkServlet.initServletBean-->
FrameworkServlet.initWebApplicationContext-->FrameworkServlet.onRefresh
-->DispatcherServlet.onRefresh-->DispatcherServlet.initStrategies-->DispatcherServlet.initHandlerMappings
-->DispatcherServlet.getDefaultStrategies-->strategies.add((T) strategy)
通过前面SpringMVC前两个流程的学习可知,DispatcherServlet继承了FrameworkServlet,同时就集成了HttpServlet,也就是说DispatcherServlet本质上就是一个Servlet,那么在容器加载这个Servlet的时候如果设置了<load-on-startup>加载时马上进行初始化,则在加载时会自动执行其init方法,于是就有了上面调用链的第一个HttpServletBean.init入口。
我们的目的是找到哪里进行了handlerMappings的核心操作,通过调用关系我们找到核心代码在DispatcherServlet.initHandlerMappings:
通过上面代码返回的对象我们可以大胆推测matchingBeans就是我们要了解的handlerMappings集合,这个方法内部完成了对多种类型HandlerMapping的初始化。通过查看我们重点关注的两种映射器对象值,可以分别看到<K,V>关系:
上面就可以看到前面我们提到的urlLookup的Map集合以及handlerMap集合。为了了解这些不同的请求地址是怎么分配给不同类型的映射器来处理的,我们继续点开这个核心方法:
实际由下面这行代码完成了:
lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit)
而真正值得思考的逻辑在于
String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
上面方法的逻辑就会自动生成一个有5种类型handlerMapping的handlerMappings,那么这5种映射器是怎么产生的呢?
通过核心代码我们可以知道,这里通过传入一个type="HandlerMapping"的类型,在beanDefinitionNames的集合中匹配所有属于HandlerMapping类型的BeanName,然后放入到result数组返回,然后在单例池中根据BeanName获取对应的HandlerMapping类型的Bean。
也就是说这些HandlerMapping映射处理器的生成是通过在BeanDefinition中匹配,如果匹配上了从单例池中获取对应类型的Bean,因此最后的handlerMappings中就有这5种类型了,至于请求路径和方法名怎么作为K,V绑定到对应映射器上的,由于个人阅读源码的能力有限这部分代码小编还尚未理解,作为小编的一个未解之谜吧。
当然,既然5种类型的映射器是通过BD来生成的,那这些BeanDefinition又是什么时候被加到Spring容器中的呢?
这就不得不提一下关于HandlerMapping映射处理器的配置文件:DispatchServlet.properties文件,这个文件就定义了哪些内置使用的组件声明。
实际上,通过上面源码的调试,对于HandlerAdaper的流程和HandlerMapping其实是很类似的。
总结
(1)handlerMappings映射器集合内部一共有5个不同类型的映射器
分别是WebMvcConfigurationSupportXXX,RequestMappingHandlerMapping和BeanNameUrlHandlerMapping,RequestMappingHandlerMapping映射处理器用于使用注解方式请求地址和方法名进行映射的Controller,SimpleUrlHandlerMapping用于实现Controller接口或实现HttpRequestHandler接口的Controller;
(2)handlerMappings映射器集合初始化会经历一个调用链
在DispatchServlet启动时自动开始初始化,
HttpServletBean.init-->initServletBean()-->FrameworkServlet.initServletBean--> FrameworkServlet.initWebApplicationContext-->FrameworkServlet.onRefresh -->DispatcherServlet.onRefresh-->DispatcherServlet.initStrategies-->DispatcherServlet.initHandlerMappings -->DispatcherServlet.getDefaultStrategies-->strategies.add((T) strategy)
初始化的结果是产生一个handlerMappings映射器集合,内部包含5种不同类型的映射器,每种映射器内部由Map<K,V>来维护一个<请求地址,全限定方法名>的映射关系;
(3)handlerMappings映射器集合的每个映射处理器
是在初始化时就生成了BeanDefinition,通过BeanDefinitionName和传入的type=HandlerMapping类型在所有BeanDefinitionNames集合中匹配所有的映射处理器,再从单例池中获取其对应Bean实例放入handlerMappings中;
(4)对于HandlerAdaper的初始化和HandlerMapping流程是类似的
(5)<请求地址全限定方法名>的映射关系
在哪里生成并维护到映射处理器中的,这部分代码小编暂时还没能了解到。
三.handlerMappings有什么扩展?
我们知道,SpringMVC中不能直接通过url来访问WEB-INF下的静态资源,如我们在工程webapp/jsp/新建一个1.jsp文件,通过
localhost:9090/jsp/1.jsp访问时会报如下404错误
但是SpringBoot却可以通过url直接访问webapp,static,resource等包下面的静态资源,为什么会这样呢?原因就在于SpringBoot对SpringMVC的handlerMappings进行了自己的拓展。
之所以SpringMVC不能访问静态资源是因为对于这种访问静态资源的请求,在handlerMappings内没有任何一个默认的映射器可以进行解析处理,因此自然这种请求就会被忽视。
对于SpringBoot来说,它自己实现了一个针对这种访问静态资源的映射处理器并交由handlerMappings来管理,因此就可以做到类似拓展,对于这个知识点在对SpringBoot学习时看能否研究一下这个特殊的映射处理器是怎么样的,这里暂时知道这么一个结论即可。
到此关于handlerMappings对象的研究学习暂时到此,回到SpringMVC的主线流程逻辑中继续学习后续的步骤。
来源:https://blog.csdn.net/qq_20395245/article/details/106365781


猜你喜欢
- 目录示例1: EncryptByAes示例2: main示例3: wrapperPublicPriviteKeyTest示例4: initH
- 前言在Android屏幕的空间中,大部分的区域我们都是可以随意绘制,只有一部分区域是显示的固定内容:状态栏标题栏(ActionBar)页面内
- 本文实例讲述了Java文件操作工具类fileUtil。分享给大家供大家参考,具体如下:package com.gcloud.common;i
- BufferedInputStream BufferedInputStream 是缓冲输入流。它继承于FilterInputSt
- Android Notification的多种用法总结我们在用手机的时候,如果来了短信,而我们没有点击查看的话,是不是在手机的最上边的状态栏
- 出现场景更新了Android Studio版本后,运行项目就出现以下警告。Warning: Mapping new ns http://sc
- /// <summary> /// 为图片生成缩略图 /// </summ
- 鉴于谷歌最新推出的Android Studio备受开发者的推崇,所以也跟着体验一下。一、介绍Android Studio Andr
- 一、介绍在日常的 web 开发中,熟悉 java 的同学一定知道,Spring MVC 可以说是目前最流行的框架,之所以如此的流行,原因很简
- 很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义Vie
- 本文实例为大家分享了android自定义波浪加载动画的具体代码,供大家参考,具体内容如下效果图1.自定义控件 WaveViewpackage
- 简述:JRebel是一款JVM插件,它使得Java代码修改后不用重启系统,立即生效。IDEA上原生是不支持热部署的,一般更新了 Java 文
- springboot开启事务很简单,只需要一个注解@Transactional 就可以了。因为在springboot中已经默认对jpa、jd
- 如执行:"2|33|4".split("|")出来的结果是:""2334奇怪吧,
- Maven热部署,顾名思义就是可以不影响项目在服务器中的运行情况,可以实现项目代码的更新,减少启动,编译时间,达到快速开发的目的,也不需要手
- #include<iostream>#include<assert.h>#include<stack>#
- 情景描述将一个时间转换为对应的unix时间戳,字符集使用UTF-8编码,数据通讯统一采用 HTTP 协议通讯,使用POST 方法请求并传递参
- 最近要搞一个项目,需要上传相册和拍照的图片,不负所望,终于完成了! 不过需要说明一下,其实网上很多教程拍照的图片,都是缩略图不是
- 简介:Android Studio是Android的官方IDE。它是专为Android而打造,可以加快您的开发速度,帮助您为每款Androi
- 经常会遇到这样的情况,我们在响应客户端请求的数据的时候需要对数据进行处理,比如数据库中的数据是int型,它可能表示某个枚举,或者其它的逻辑意