昨天简单分析了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 (二)的更多相关文章

  1. SpringMvc如何将Url 映射到 RequestMapping (一)

    SpringMvc Url 匹配规则详解 最近开始阅读Spring 源码,虽然用了很久的spring ,但是没有真正的分析过Spring时如何工作的.今天重 MVC 的Url匹配规则开始进行Sprin ...

  2. SpringMVC中url映射到Controller

    SpringMVC也是一种基于请求驱动的WEB框架,并且使用了前端控制器的设计模式.前端控制器就是DispatcherServlet控制器,只要满足web.xml文件中的[url-pattern]的规 ...

  3. SpringMvc的Url映射和传参案例(转)

    Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...

  4. SpringMvc的Url映射和传参案例

    Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...

  5. 应用springMVC时如果配置URL映射时如下配置

    应用springMVC时如果配置URL映射时如下配置 [html] view plaincopy<servlet> <servlet-name>appServlet</s ...

  6. 关于requestMapping 进行url映射实现小小知识点 以及如何获取请求的url中的参数

    requstMapping 用来处理url映射  可以作用在controller类上  也可以作用在方法上 经常使用的方式  通过接收一种映射关系 @RequestMapping("/del ...

  7. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)--S ...

  8. SpringMVC基础03——常用注解之@RequestMapping

    1.用法 SpringMVC使用@RequestMapping注解,为控制器指定可以处理哪些URL请求,并且可以指定处理请求的类型(POST/GET),如果@RequestMapping没有指定请求的 ...

  9. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序(一)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)——S ...

随机推荐

  1. 【转载】TCP和TCP/IP的区别

    TCP/IP协议(Transmission Control Protocol/Internet Protocol)叫做传输控制/网际协议, 又叫网络通讯协议,这个协议是Internet国际互联网络的基 ...

  2. leetcode:238. Product of Array Except Self(Java)解答

    转载请注明出处:z_zhaojun的博客 原文地址 题目地址 Product of Array Except Self Given an array of n integers where n > ...

  3. 动态标绘演示系统1.4.3(for ArcGIS Flex)

    标绘有API文档啦! 在线浏览 ------------------------------------------------------------------------------------ ...

  4. Use Local Or Global Index?

    常常我们须要将大表依据分区键进行分区,当建立索引的时候.我们究竟使用local 还是global 索引呢 先看看两种索引的特点: 本地索引特点: 1. 本地索引一定是分区索引.分区键等同于表的分区键. ...

  5. Linux下使用inotify实现对文件的监控

    项目中,要实现用户通过网页设置參数,后台接收数据然后写串口. 网页写数据到本地文件,使用inotify监控文件的IN_MODIFY事件.当文件被改动,然后触发写串口事件. 第一个程序只把要监控的文件增 ...

  6. 系统队列中的Windows错误报告

  7. linux 多个文件中查找字符串 hadoop 3 安装 调试

    http://www.cnblogs.com/iLoveMyD/p/4281534.html 2015年2月9日 14:36:38 # find <directory> -type f - ...

  8. hdu 3507(DP+斜率优化)

    Print Article Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)To ...

  9. UI:数据的解析XML与JSON

    XML  和  JSON 语言  本篇博客来自互联网参考 XML 和 JSON 的互相转化 有属性的转化为对象,无属性的转化为字符串 节点的顺序性不可逆,XML有顺序,JSON 无顺序 XML 和 J ...

  10. [App Store Connect帮助]七、在 App Store 上发行(2.3)设定价格与销售范围:为您的 App 选择地区

    您可以选择希望您的 App 在 App Store 上可用的地区.默认情况下,所有地区都被选中,但您可以取消选中您不想销售您 App 的地区.新地区或已更改地区的 App Store 会在 24 小时 ...