SpringMVC中的handlerMappings对象用法

目录
  • 一.handlerMappings集合内部的元素是什么,有什么作用?
    • 对于RequestMappingHandlerMapping
    • 对于SimpleUrlHandlerMapping
  • 二.handlerMappings集合是怎么初始化的?
    • 总结
  • 三.handlerMappings有什么扩展?
    • localhost:9090/jsp/1.jsp访问时会报如下404错误

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

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

(0)

相关推荐

  • Spring MVC学习教程之RequestMappingHandlerAdapter详解

    前言 RequestMappingHandlerAdapter实现了HandlerAdapter接口,顾名思义,表示handler的adapter,这里的handler指的是Spring处理具体请求的某个Controller的方法,也就是说HandlerAdapter指的是将当前请求适配到某个Handler的处理器.RequestMappingHandlerAdapter是HandlerAdapter的一个具体实现,主要用于将某个请求适配给@RequestMapping类型的Handler处理.

  • Spring MVC学习教程之RequestMappingHandlerMapping匹配

    前言 对于RequestMappingHandlerMapping,使用Spring的同学基本都不会陌生,该类的作用有两个: 通过request查找对应的HandlerMethod,即当前request具体是由Controller中的哪个方法进行处理: 查找当前系统中的Interceptor,将其与HandlerMethod封装为一个HandlerExecutionChain. 本文主要讲解RequestMappingHandlerMapping是如何获取HandlerMethod和Interc

  • SpringMVC源码解读之HandlerMapping - AbstractUrlHandlerMapping系列request分发

    AbstractHandlerMapping实现HandlerMapping接口定的getHandler 1. 提供getHandlerInternal模板方法给子类实现      2. 如果没有获取Handler,则使用默认的defaultHandler 3. 如果handler是string类型,从context获取实例 4. 通过getHandlerExecutionChain封装handler,添加interceptor // AbstractHandlerMapping /** * L

  • SpringMVC源码解读之HandlerMapping

    概述 对于Web开发者,MVC模型是大家再熟悉不过的了,SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程时写的controller)以及一系列的拦截器interceptors,此时DispatcherServlet会根据返

  • SpringMVC中的handlerMappings对象用法

    目录 一.handlerMappings集合内部的元素是什么,有什么作用? 对于RequestMappingHandlerMapping 对于SimpleUrlHandlerMapping 二.handlerMappings集合是怎么初始化的? 总结 三.handlerMappings有什么扩展? localhost:9090/jsp/1.jsp访问时会报如下404错误 从SpringMVC源码解析所用的例子,一个http://localhost:9090/web/hi?name=yang请求调

  • SpringMVC中的Model对象用法说明

    模型对象的作用主要是保存数据,可以借助它们将数据带到前端. 常用的模型对象有以下几个: ModelAndView(顾名思义,模型和视图,既可以携带数据信息,也可以携带视图信息,常规用法如下) /** * ModelAndView 绑定数据到视图 (ModelMap用于传递数据 View对象用于跳转) * @return * @throws Exception */ @RequestMapping(value="/case2") public ModelAndView case2() t

  • ASP.NET中Application全局对象用法实例浅析

    本文实例讲述了ASP.NET中Application全局对象用法.分享给大家供大家参考.具体如下: Application是应用全局对象,被全体共享.无论通过哪个页面操作Application,另一个页面都可以读取Application信息. 由于Application是共享的,操作之前先Lock,操作完成后UnLock. 在一个页面设置数据: Application.Lock(); Application.Set("address", "上海"); Applica

  • Java中Map集合中的Entry对象用法

    Entry: 键值对 对象. 在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry.Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值. Entry为什么是静态的? Entry是Map接口中提供的一个静态内部嵌套接口,修饰为静态可以通过类名调用. Map集合遍历键值对的方式: Set<Map.Entry<K,V>> entrySet(); //返回此映射中包含的映射

  • keras中的History对象用法

    keras中的fit_generator和fit函数均返回History对象,那么History怎么用呢?事实上History对象已经记录了运行输出.在了解之前,我们甚至自己定义回调函数记录损失和准确率等. 相关keras源码位于网址: class History(Callback): """Callback that records events into a `History` object. This callback is automatically applied

  • C++中Semaphore内核对象用法实例

    本文实例讲述了C++中Semaphore内核对象的用法,分享给大家供大家参考.具体方法如下: 复制代码 代码如下: // Semaphore.cpp : 定义控制台应用程序的入口点.  //    #include "stdafx.h"  #include <Windows.h>  #include <process.h>     HANDLE g_hSemaphore;  DWORD g_nConut1 = 0;  DWORD g_nConut2 = 0; 

  • Spring IOC中的Bean对象用法

    目录 Spring IOC中的Bean对象 一.Bean是什么 二.Bean对象的三种构造方式 三.依赖注入 四.Bean的生命周期 Ioc中Bean的作用域 bean的作用范围和生命周期 Spring IOC中的Bean对象 一.Bean是什么 突然发现提到了好多次Bean,居然忘记了讲Bean是什么.没事,现在讲也不晚.Java中的Bean是一种规范,是一种特殊的java类.所以我们先来看看Bean的规范. Bean必须生成public class类. 所有属性必须封装,Bean类的属性都为

  • 浅谈SpringMVC中的session用法及细节记录

    前言 初学SpringMVC,最近在给公司做的系统做登录方面,需要用到session. 在网上找了不少资料,大致提了2点session保存方式: 1.javaWeb工程通用的HttpSession 2.SpringMVC特有的@SessionAttributes 我个人比较关注@SessionAttributes的用法,毕竟现在是在用SpringMVC嘛.但是我看网上那些文章,基本都是只说明了基础用法,详细的使用和细节却基本没有,我想这是不够的,所以我自己做了一些测试,然后整理了下代码做了个de

  • javascript中clipboardData对象用法详解

    本文实例讲述了javascript中clipboardData对象用法.分享给大家供大家参考.具体分析如下: clipboardData对象  ,注意网页里剪贴板到现在只能设置Text类型,即只能复制文本 clearData("Text")清空粘贴板 getData("Text")读取粘贴板的值 setData("Text",val)设置粘贴板的值 当复制的时候body的oncopy事件被触发,直接return false就是禁止复制,注意是不能

  • JavaScript中Array对象用法实例总结

    本文实例讲述了JavaScript中Array对象用法.分享给大家供大家参考,具体如下: Array数组对象有很多常用的方法和属性,现总结如下: 1. length属性,获取数组中元素的个数. 2. concat()方法,连接两个数组.将两个数组连接起来.示例如下: var names= new Array('Jack','Tom','Jim'); var ages= new Array(12,32,44); var concatArray; concatArray=names.concat(a

随机推荐