SpringMvc如何将Url 映射到 RequestMapping (二)
昨天简单分析了Springmvc 中 RequestMapping 配置的url和请求url之间的匹配规则。今天详细的跟踪一下一个请求url如何映射到Controller的对应方法上
一、入口 org.springframework.web.servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try {
ModelAndView mv = null;
Exception dispatchException = null; try {
// 检查是否文件上传 即 contentType = "multipart/###"
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request); // 进入获取 mappedHandler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
} // Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) {
return;
} applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} }
从doDispatch方法入口(DispatcherServlet 处理request请求入口)
org.springframework.web
servlet.DispatcherServlet.doDispatch(HttpServletRequest, HttpServletResponse)
servlet.DispatcherServlet.getHandler(HttpServletRequest)
servlet.handler.AbstractHandlerMapping.getHandler(HttpServletRequest)
servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(HttpServletRequest)
servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(String, HttpServletRequest)
servlet.mvc.method.RequestMappingInfoHandlerMapping.handleMatch(RequestMappingInfo, String, HttpServletRequest)
解析url和requestMapping 直接的映射关系,这个时候我们发现spring将 RequestMapping定义的url都存储在 MappingRegistry 这个类中,接下来先解析一下 这个类具体储存了那些数据。
mappingLookup = new LinkedHashMap<T, HandlerMethod>()
内部数据结构 {[/test]}=public java.lang.String xiaolang.TestController.test()
urlLookup = new LinkedMultiValueMap<String, T>()
内部数据结构 /test=[{[/test]}]
根据调试内容可以分析
urlLookup 的key 和请求url进行完全匹配或者正则匹配 获取 定义的RequestMapping
mappingLookup 通过获取的RequestMapping 匹配对应的Controller
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
// 如果匹配成功 则加载match
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
// 没有全路径匹配 则进行正则匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
} if (!matches.isEmpty()) {
// 将左右匹配到的规则进行 排序 (排序规则请参考上一篇随笔)
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
Collections.sort(matches, comparator);
Match bestMatch = matches.get(0);
// 如果 匹配规则大于 1个 即两个 patten 同时满足 url 且 匹配度一致
// 则抛出异常
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
// 根据 最佳匹配的 url中 将url中的参数等信息封装到 handlermethod 中 此时已经找到了最佳Controller.method
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
接下来我们跟踪一下具体解析逻辑:
1、完全匹配
this.mappingRegistry.getMappingsByUrl(lookupPath); - > urlLookup.get(urlPath)
从mappingRegistry 查看是否有完全匹配的路径,(在上一篇文章中分析过,完全匹配优先级最高)
如果匹配成功过
则 addMatchingMappings(directPathMatches, matches, request); -> mappingLookup(次数省略了部分校验判断)
从mappingLookup中找到对应Controller的method
2、正则匹配
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
如果没有匹配成功,则对所有的定义的url进行正则匹配(此处只进行规则匹配)
3、将所有匹配结果进行优先级排序(排序规则在上一篇文章中已经详细描述)
获取最佳匹配规则 和 第二匹配规则进行比较,如果最佳匹配规则和第二匹配规则优先级相同,即spring无法确定应该使用哪个Controller进行处理,此时程序抛出异常将提示错误
4、封装请求结果
handleMatch(bestMatch.mapping, lookupPath, request);
将请求结果进行封装,包括请求URL参数、等相关信息。
5、根据请求结果返回对应的Controller SpringBean
handlerMethod.createWithResolvedBean()
综合上面debug 调试
1、我们已经找到了最佳匹配的method 且发现,spring 在启动时已经将 url method 等相关映射信息保存在了 MappingRegistry 中
2、如果出现规则匹配度一致的url,系统会提示错误,所以尽量避免 定义比较广泛匹配的RequestMapping
SpringMvc如何将Url 映射到 RequestMapping (二)的更多相关文章
- SpringMvc如何将Url 映射到 RequestMapping (一)
SpringMvc Url 匹配规则详解 最近开始阅读Spring 源码,虽然用了很久的spring ,但是没有真正的分析过Spring时如何工作的.今天重 MVC 的Url匹配规则开始进行Sprin ...
- SpringMVC中url映射到Controller
SpringMVC也是一种基于请求驱动的WEB框架,并且使用了前端控制器的设计模式.前端控制器就是DispatcherServlet控制器,只要满足web.xml文件中的[url-pattern]的规 ...
- SpringMvc的Url映射和传参案例(转)
Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...
- SpringMvc的Url映射和传参案例
Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...
- 应用springMVC时如果配置URL映射时如下配置
应用springMVC时如果配置URL映射时如下配置 [html] view plaincopy<servlet> <servlet-name>appServlet</s ...
- 关于requestMapping 进行url映射实现小小知识点 以及如何获取请求的url中的参数
requstMapping 用来处理url映射 可以作用在controller类上 也可以作用在方法上 经常使用的方式 通过接收一种映射关系 @RequestMapping("/del ...
- Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)--S ...
- SpringMVC基础03——常用注解之@RequestMapping
1.用法 SpringMVC使用@RequestMapping注解,为控制器指定可以处理哪些URL请求,并且可以指定处理请求的类型(POST/GET),如果@RequestMapping没有指定请求的 ...
- Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序(一)
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)——S ...
随机推荐
- 【转载】TCP和TCP/IP的区别
TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议, 又叫网络通讯协议,这个协议是Internet国际互联网络的基 ...
- leetcode:238. Product of Array Except Self(Java)解答
转载请注明出处:z_zhaojun的博客 原文地址 题目地址 Product of Array Except Self Given an array of n integers where n > ...
- 动态标绘演示系统1.4.3(for ArcGIS Flex)
标绘有API文档啦! 在线浏览 ------------------------------------------------------------------------------------ ...
- Use Local Or Global Index?
常常我们须要将大表依据分区键进行分区,当建立索引的时候.我们究竟使用local 还是global 索引呢 先看看两种索引的特点: 本地索引特点: 1. 本地索引一定是分区索引.分区键等同于表的分区键. ...
- Linux下使用inotify实现对文件的监控
项目中,要实现用户通过网页设置參数,后台接收数据然后写串口. 网页写数据到本地文件,使用inotify监控文件的IN_MODIFY事件.当文件被改动,然后触发写串口事件. 第一个程序只把要监控的文件增 ...
- 系统队列中的Windows错误报告
- linux 多个文件中查找字符串 hadoop 3 安装 调试
http://www.cnblogs.com/iLoveMyD/p/4281534.html 2015年2月9日 14:36:38 # find <directory> -type f - ...
- hdu 3507(DP+斜率优化)
Print Article Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)To ...
- UI:数据的解析XML与JSON
XML 和 JSON 语言 本篇博客来自互联网参考 XML 和 JSON 的互相转化 有属性的转化为对象,无属性的转化为字符串 节点的顺序性不可逆,XML有顺序,JSON 无顺序 XML 和 J ...
- [App Store Connect帮助]七、在 App Store 上发行(2.3)设定价格与销售范围:为您的 App 选择地区
您可以选择希望您的 App 在 App Store 上可用的地区.默认情况下,所有地区都被选中,但您可以取消选中您不想销售您 App 的地区.新地区或已更改地区的 App Store 会在 24 小时 ...