前言

SpringMVC是目前主流的Web MVC框架之一。

我们使用浏览器通过地址 http://ip:port/contextPath/path进行访问,SpringMVC是如何得知用户到底是访问哪个Controller中的方法,这期间到底发生了什么。

本文将分析SpringMVC是如何处理请求与Controller之间的映射关系的,让读者知道这个过程中到底发生了什么事情。

源码分析

在分析源码之前,我们先了解一下几个东西。

1.这个过程中重要的接口和类。

HandlerMethod类:

Spring3.1版本之后引入的。是一个封装了方法参数、方法注解,方法返回值等众多元素的类。

它的子类InvocableHandlerMethod有两个重要的属性WebDataBinderFactory和HandlerMethodArgumentResolverComposite, 很明显是对请求进行处理的。

InvocableHandlerMethod的子类ServletInvocableHandlerMethod有个重要的属性HandlerMethodReturnValueHandlerComposite,很明显是对响应进行处理的。

ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。  (RequestMappingHandlerAdapter源码,实例化ServletInvocableHandlerMethod的时候分别set了上面提到的重要属性)

MethodParameter类:

HandlerMethod类中的parameters属性类型,是一个MethodParameter数组。MethodParameter是一个封装了方法参数具体信息的工具类,包括参数的的索引位置,类型,注解,参数名等信息。

HandlerMethod在实例化的时候,构造函数中会初始化这个数组,这时只初始化了部分数据,在HandlerAdapter对请求处理过程中会完善其他属性,之后交予合适的HandlerMethodArgumentResolver接口处理。

以类DeptController为例:

@Controller
@RequestMapping(value = "/dept")
public class DeptController {@Autowiredprivate IDeptService deptService;@RequestMapping("/update")@ResponseBodypublic String update(Dept dept) {deptService.saveOrUpdate(dept);return "success";}}

(刚初始化时的数据)

(HandlerAdapter处理后的数据)

RequestCondition接口:

Spring3.1版本之后引入的。是SpringMVC的映射基础中的请求条件,可以进行combine, compareTo,getMatchingCondition操作。这个接口是映射匹配的关键接口,其中getMatchingCondition方法关乎是否能找到合适的映射。

RequestMappingInfo类:

Spring3.1版本之后引入的。是一个封装了各种请求映射条件并实现了RequestCondition接口的类。

有各种RequestCondition实现类属性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,这个请求条件看属性名也了解,分别代表http请求的路径模式、方法、参数、头部等信息。

RequestMappingHandlerMapping类:

处理请求与HandlerMethod映射关系的一个类。

2.Web服务器启动的时候,SpringMVC到底做了什么。

先看AbstractHandlerMethodMapping的initHandlerMethods方法中。

我们进入createRequestMappingInfo方法看下是如何构造RequestMappingInfo对象的。

PatternsRequestCondition构造函数:

类对应的RequestMappingInfo存在的话,跟方法对应的RequestMappingInfo进行combine操作。

然后使用符合条件的method来注册各种HandlerMethod。

下面我们来看下各种RequestCondition接口的实现类的combine操作。

PatternsRequestCondition:

RequestMethodsRequestCondition:

方法的请求条件,用个set直接add即可。

其他相关的RequestConditon实现类读者可自行查看源码。

最终,RequestMappingHandlerMapping中两个比较重要的属性

private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();

private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();

T为RequestMappingInfo。

构造完成。

我们知道,SpringMVC的分发器DispatcherServlet会根据浏览器的请求地址获得HandlerExecutionChain。

这个过程我们看是如何实现的。

首先看HandlerMethod的获得(直接看关键代码了):

这里的比较器是使用RequestMappingInfo的compareTo方法(RequestCondition接口定义的)。

然后构造HandlerExecutionChain加上拦截器

实例

写了这么多,来点例子让我们验证一下吧。

@Controller
@RequestMapping(value = "/wildcard")
public class TestWildcardController {@RequestMapping("/test/**")@ResponseBodypublic String test1(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "TestWildcardController -> /test/**");return view;}@RequestMapping("/test/*")@ResponseBodypublic String test2(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "TestWildcardController -> /test*");return view;}@RequestMapping("test?")@ResponseBodypublic String test3(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "TestWildcardController -> test?");return view;}@RequestMapping("test/*")@ResponseBodypublic String test4(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "TestWildcardController -> test/*");return view;}}

由于这里的每个pattern都带了*因此,都不会加入到urlMap中,但是handlerMethods还是有的。

当我们访问:http://localhost:8888/SpringMVCDemo/wildcard/test1的时候。

会先根据 "/wildcard/test1" 找urlMap对应的RequestMappingInfo集合,找不到的话取handlerMethods集合中所有的key集合(也就是RequestMappingInfo集合)。

然后进行匹配,匹配根据RequestCondition的getMatchingCondition方法。

最终匹配到2个RequestMappingInfo:

然后会使用比较器进行排序。

之前也分析过,比较器是有优先级的。

我们看到,RequestMappingInfo除了pattern,其他属性都是一样的。

我们看下PatternsRequestCondition比较的逻辑:

因此,/test*的通配符比/test?的多,因此,最终选择了/test?

直接比较优先于通配符。

@Controller
@RequestMapping(value = "/priority")
public class TestPriorityController {@RequestMapping(method = RequestMethod.GET)@ResponseBodypublic String test1(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "其他condition相同,带有method属性的优先级高");return view;}@RequestMapping()@ResponseBodypublic String test2(ModelAndView view) {view.setViewName("/test/test");view.addObject("attr", "其他condition相同,不带method属性的优先级高");return view;}}

这里例子,其他requestCondition都一样,只有RequestMethodCondition不一样。

看出,方法多的优先级越多。

至于其他的RequestCondition,大家自行查看源码吧。

资源文件映射

以上分析均是基于Controller方法的映射(RequestMappingHandlerMapping)。

SpringMVC中还有静态文件的映射,SimpleUrlHandlerMapping。

DispatcherServlet找对应的HandlerExecutionChain的时候会遍历属性handlerMappings,这个一个实现了HandlerMapping接口的集合。

由于我们在*-dispatcher.xml中加入了以下配置:

<mvc:resources location="/static/" mapping="/static/**"/>

Spring解析配置文件会使用ResourcesBeanDefinitionParser进行解析的时候,会实例化出SimpleUrlHandlerMapping。

其中注册的HandlerMethod为ResourceHttpRequestHandler。

访问地址:http://localhost:8888/SpringMVCDemo/static/js/jquery-1.11.0.js

地址匹配到/static/**。

最终SimpleUrlHandlerMapping找到对应的Handler -> ResourceHttpRequestHandler。

ResourceHttpRequestHandler进行handleRequest的时候,直接输出资源文件的文本内容。

总结

大致上整理了一下SpringMVC对请求的处理,包括其中比较关键的类和接口,希望对读者有帮助。

让自己对SpringMVC有了更深入的认识,也为之后分析数据绑定,拦截器、HandlerAdapter等打下基础。

面试官:你能告诉我一个请求过来,Spring MVC 是如何找到正确的 Controller 的?相关推荐

  1. 以资深面试官的角度告诉大家面试前可以做的准备

    本人之前写了三篇博文,从面试官角度来告诉大家,哪些人能面试成功,你的简历能帮你争取到面试机会吗,以及从面试官角度告诉大家如何准备项目方面的描述,均得到了比较好的反响.这里我们就从面试流程入手,告诉大家 ...

  2. 【华为笔试】安排面试官。有M个面试官,每个面试官熟悉的编程语言是一个列表,有N个面试者,按照面试者的机试选择的语言分配面试官进行面试(Python)

    题目 有M个面试官,每个面试官熟悉的编程语言是一个列表,比如["Java", "C++", "Golang"]表示该面试官熟悉Java.C+ ...

  3. 对K个不同字符的全排列组成的数组, 面试官从中随机拿走了一个, 剩下的数组作为输入, 请帮忙找出这个被拿走的字符串?

    对K个不同字符的全排列组成的数组, 面试官从中随机拿走了一个, 剩下的数组作为输入, 请帮忙找出这个被拿走的字符串? 比如["ABC", "ACB", &quo ...

  4. 字节面试官:如何实现Ajax并发请求控制

    偷偷告诉你,点此抽奖送红包还送3本比红宝书还贵的书 实现一个批量请求函数 multiRequest(urls, maxNum),要求如下: • 要求最大并发数 maxNum • 每当有一个请求返回,就 ...

  5. java 实体类包含list 怎么取值_舅舅是面试官,偷偷告诉你们面试官最爱问的Java面试题...

    2015 年,因为工作岗位的变动,舅舅开始负责给集团招聘一些技术人员,出于对公司的负责,也为了更好的胜任技术经理的职位,在面试的这件事上,舅舅做了大量的"功课",首先研究了几乎所有 ...

  6. 面试官 | SpringBoot 中如何实现异步请求和异步调用?

    作者 | 会炼钢的小白龙 来源 | cnblogs.com/baixianlong/p/10661591.html 一.SpringBoot中异步请求的使用 1.异步请求与同步请求 特点: 可以先释放 ...

  7. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  8. 字节跳动面试官:请你实现一个大文件上传和断点续传

    前言 这段时间面试官都挺忙的,频频出现在博客文章标题,虽然我不是特别想蹭热度,但是实在想不到好的标题了-.-,蹭蹭就蹭蹭

  9. local tomcat 找不到springmvc里的包_唰唰的手撕一个简单的Spring Mvc 框架

    @[TOC] 自定义实现Spring Mvc框架 前言 在使用Spring Mvc时候我们需要配置一个中央处理器DispatcherServlet用于分发请求,根据url查找处理器,然后再根据处理器 ...

最新文章

  1. 《ASP.NET MVC 4 实战》----导读
  2. windows下给定多个openNi的Uri,匹配与uri对应的Uvc RGB设备
  3. 【BZOJ 2119】 2119: 股市的预测 (后缀数组+分块+RMQ)
  4. ua获取手机型号_取证人员为什么很难从移动设备上获取电子数据证据?
  5. 理解分布式一致性:Paxos协议之Cheap Paxos Fast Paxos
  6. python如何更改entry属性_如何在Python3中更改Gtk3 Entry文本颜色?
  7. Python URL编码
  8. vb中多个串口通讯_串口服务器的原理及应用!
  9. python定义类的程序_python扫码签到程序python中如何定义类
  10. kafka producer写入超时
  11. Linux查看和注销用户
  12. 《微店赚钱一册通 ——开店+营销+推广 实战全攻略》一一1.5 微店有哪些优势...
  13. c语言打印字符数据在屏幕上,在屏幕上输出各种类型的数据
  14. 教学管理系统java_Java 实现简易教务管理系统的代码
  15. php计算圆的面积怎么带单位_圆的面积计算公式的教学演示工具的制作方法
  16. 六一儿童节 python
  17. 基于lstm+crf实现电子病历实体信息识别 完整的代码+数据集+说明 毕设
  18. st_contains
  19. ubuntu空间扩容--grub修复之boot-repair修复
  20. 海康威视工业相机SDK二次开发环境配置—Windows10+VS2017

热门文章

  1. Python基础教程(十二):GUI编程、版本区别、IDE
  2. 2015年浪潮面试题
  3. 二叉树的先序/中序/后序/层次遍历
  4. Apache httpd Server 配置正向代理
  5. JQuery 判断checkbox是否选中,checkbox全选,获取checkbox选中值
  6. 在Xcode6中添加prefix.pch文件
  7. java笔记之抽象类和接口
  8. JS Math对象中一些小技巧
  9. 两不同网段主机直连通信过程的建立(3个实验详细分析)
  10. SQL培训内容转之wantin6(收藏)