详解SpringMVC请求的时候是如何找到正确的Controller
详解SpringMVC请求的时候是如何找到正确的Controller[附带源码分析]
目录
前言
SpringMVC是目前主流的Web MVC框架之一。
如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html
我们使用浏览器通过地址 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 {
@Autowired
private IDeptService deptService;
@RequestMapping("/update")
@ResponseBody
public 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/**")
@ResponseBody
public String test1(ModelAndView view) {
view.setViewName("/test/test");
view.addObject("attr", "TestWildcardController -> /test/**");
return view;
}
@RequestMapping("/test/*")
@ResponseBody
public String test2(ModelAndView view) {
view.setViewName("/test/test");
view.addObject("attr", "TestWildcardController -> /test*");
return view;
}
@RequestMapping("test?")
@ResponseBody
public String test3(ModelAndView view) {
view.setViewName("/test/test");
view.addObject("attr", "TestWildcardController -> test?");
return view;
}
@RequestMapping("test/*")
@ResponseBody
public 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)
@ResponseBody
public String test1(ModelAndView view) {
view.setViewName("/test/test");
view.addObject("attr", "其他condition相同,带有method属性的优先级高");
return view;
}
@RequestMapping()
@ResponseBody
public 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等打下基础。
参考资料
详解SpringMVC请求的时候是如何找到正确的Controller的更多相关文章
- 详解SpringMVC请求的时候是如何找到正确的Controller[附带源码分析]
目录 前言 源码分析 重要接口介绍 SpringMVC初始化的时候做了什么 HandlerExecutionChain的获取 实例 资源文件映射 总结 参考资料 前言 SpringMVC是目前主流的W ...
- 详解SpringMVC
详解SpringMVC 一.什么是MVC? MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范.就是将业务逻辑.数据.显示分离的方法来组织代码. ...
- TOMCAT原理详解及请求过程(转载)
转自https://www.cnblogs.com/hggen/p/6264475.html TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Ser ...
- ASP.NET 运行时详解 揭开请求过程神秘面纱
对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...
- ***PHP $_FILES函数详解 + PHP文件上传 move_uploaded_file() 参数的正确写法
PHP $_FILES函数详解 在PHP中上传一个文件建一个表单要比ASP中灵活得多.具体的看代码. 如: 复制代码代码如下: <form enctype="multipart/fo ...
- SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller
SpringMVC是目前主流的Web MVC框架之一. 我们使用浏览器通过地址 http://ip:port/contextPath/path进行访问,SpringMVC是如何得知用户到底是访问哪个 ...
- 详解SpringMVC中GET请求
GET请求概述 GET请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接.URL的编码格式采用的是ASCII编码,而不是uniclde,所有的非ASCII字符都要编 ...
- 详解SpringMVC中Controller的方法中参数的工作原理[附带源码分析]
目录 前言 现象 源码分析 HandlerMethodArgumentResolver与HandlerMethodReturnValueHandler接口介绍 HandlerMethodArgumen ...
- TOMCAT原理详解及请求过程
Tomcat: Tomcat是一个JSP/Servlet容器.其作为Servlet容器,有三种工作模式:独立的Servlet容器.进程内的Servlet容器和进程外的Servlet容器. Tomcat ...
随机推荐
- POJ 1066 Treasure Hunt(相交线段&&更改)
Treasure Hunt 大意:在一个矩形区域内.有n条线段,线段的端点是在矩形边上的,有一个特殊点,问从这个点到矩形边的最少经过的线段条数最少的书目,穿越仅仅能在中点穿越. 思路:须要巧妙的转换一 ...
- NYNU_省赛选拔题(6)
题目描述 有一天,小米找到了一个藏宝的迷宫地图,迷宫在一个沙漠里有,迷宫里面有许多宝藏.迷宫里可能有N个藏宝地点,用1到K标记.藏宝地点之间最多有一条通路相连.标记1为迷宫的进出口. 他已经知道其中K ...
- 【超酷超实用】CSS3可滑动跳转的分页插件制作教程
原文:[超酷超实用]CSS3可滑动跳转的分页插件制作教程 今天我要向大家分享一款很特别的CSS3分页插件,这款分页插件不仅可以点击分页按钮来实现分页,而且可以滑动滑杆来实现任意页面的跳转,看看都非常酷 ...
- linux后台server开发环境的部署配置和验证(nginx+apache+php-fpm+FASTCGI(C/C++))
linux后台server开发环境部署配置 引言 背景 随着互联网业务的不断增多.开发环境变得越来越复杂,为了便于统一server端的开发部署环境,特制定本配置文档. 使用软件 CentOS 6.3( ...
- ASP.NET 5 (vNext)
ASP.NET 5 (vNext) 理解和入门 概述 ASP.NET 5 (又称为vNext) 是自ASP.NET产生15年以来一次革命性的更新, 我们可以从以下几点来理解其概貌和意义: ASP. ...
- linux_apt-get 使用详解
安装应用从互联网上下载查询时 用到,今天安装个 mysql 简化流程如下: apt-cache search mysql-server sudo apt-get install mysql-serve ...
- nginx基础入门
nginx常常被用来处理静态资源如css.js.图片.html等,也被用作反向代理server.邮件server,也时常拿来做负载均衡.它的优势主要体如今对静态资源的处理上,这次抽出了点时间整理了一些 ...
- 了解你的家公家IP
我们总是在不在家的时候,须要訪问我们的电脑或设备,因为大多数人拥有来自ISP的动态IP,我们能够做一个小型设备来给我们的Android手机发送一个简单的通知,这样我们就能够总有IP用了,有 ...
- 【高德地图API】那些年我们一起开发的APP—即LBS应用模式分享
原文:[高德地图API]那些年我们一起开发的APP—即LBS应用模式分享 摘要:利用地图API都能做些什么应用呢?应用商店里所有的分类,都可以结合上LBS来丰富应用.除了传统的生活服务应用,还有新潮的 ...
- Struts2 整合jQuery实现Ajax功能(2)
1.1.1 Action利用struts2-json-plugin-X.X.X.jar响应Json格式信息: 1. function removerecordbyid(recordid) ...