SpringMVC中的handlerMappings对象用法

 更新时间:2021年9月26日 16:00  点击:2432

从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的主线流程逻辑中继续学习后续的步骤。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • java8如何用Stream查List对象某属性是否有重复

    这篇文章主要介绍了java8如何用Stream查List对象某属性是否有重复的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-11
  • R语言 如何删除指定变量或对象

    这篇文章主要介绍了R语言删除指定变量或对象的操作方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • JavaScript预解析,对象详解

    这篇文章主要介绍了JavaScript预解析,对象的的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助...2021-11-10
  • 牛叉的Jquery――Jquery与DOM对象的互相转换及DOM的三种操作

    只有jQuery对象才能调用jQuery类库的各种函数,同样有些dom对象的属性和方法在jQuery上也是无法调用的,不过基本上jQuery类库提供的函数包含了所有的dom操作。这就需要我们知道如何将jQuery对象和DOM的相互转换的方法。1...2015-10-30
  • js如何打印object对象

    js调试中经常会碰到输出的内容是对象而无法打印的时候,光靠alert只能打印出object标示,却不能打印出来里面的内容,甚是不方便,于是各方面整理总结了如下一个函数,能够将数组或者对象这类的结果一一打印出来,具体代码如下: fu...2015-10-21
  • javascript self对象使用详解

    这篇文章主要介绍了javascript self对象使用详解的相关资料,需要的朋友可以参考下...2016-10-20
  • 替换json对象中的key最佳方案

    本文给大家介绍如何替换json对象中的key,通过实例代码给大家介绍key的替换方法,代码也很简单,需要的朋友参考下吧...2021-06-02
  • ActiveX部件不能创建对象:dm.dmsoft代码:800A01AD

    vbs调用插件报:ActiveX部件不能创建对象,代码:800A01AD,一般是因为病毒导致dll文件丢失或者64系统问题导致,需要的朋友可以参考下...2020-06-30
  • Xml中使用foreach遍历对象实现代码

    这篇文章主要介绍了Xml中使用foreach遍历对象实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-04
  • 理解JavaScript事件对象

    这篇文章主要为大家介绍了JavaScript事件对象,了解JavaScript事件...2016-01-26
  • JS实现简单面向对象的颜色选择器实例

    这篇文章主要介绍了JS实现简单面向对象的颜色选择器,以完整实例形式分析了JavaScript基于面向对象实现颜色选择器的具体步骤与实现技巧,需要的朋友可以参考下...2016-04-23
  • 解决SpringCloud Feign传对象参数调用失败的问题

    这篇文章主要介绍了解决SpringCloud Feign传对象参数调用失败的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-24
  • vue - props 声明数组和对象操作

    这篇文章主要介绍了vue - props 声明数组和对象操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-30
  • 对象题目的一个坑 理解Javascript对象

    这篇文章主要介绍了Javascript对象,特别为大家分享了对象题目的一个坑,提供了解题思路,感兴趣的小伙伴们可以参考一下...2015-12-24
  • HTML5 FileReader对象的具体使用方法

    这篇文章主要介绍了HTML5 FileReader对象的具体使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
  • JavaScript Blob对象原理及用法详解

    这篇文章主要介绍了JavaScript Blob对象原理及用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-10-14
  • Bootstrap媒体对象的实现

    在web页面中,图片居左,内容居右排列,是非常常见的效果,它也就是媒体对象,它是一种抽象的样式,可以用来构建不同类型的组件。本文给大家介绍Bootstrap媒体对象的实现,感兴趣的朋友一起学习吧...2016-05-04
  • 浅谈JS正则RegExp对象

    这篇文章主要介绍JS正则RegExp对象,正则表达式是描述字符模式的对象,用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。下面就来看具体详情,需要的朋友可以参考一下...2021-10-21
  • javascript创建对象的几种模式介绍

    下面小编就为大家带来一篇javascript创建对象的几种模式介绍。小编觉得挺不错的,现在分享给大家,也给大家做个参考...2016-05-09