昨天简单分析了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. HDU 2896 病毒侵袭 (AC自己主动机)

    pid=2896">http://acm.hdu.edu.cn/showproblem.php?pid=2896 病毒侵袭 Time Limit: 2000/1000 MS (Java ...

  2. LINQ体验(1)——Visual Studio 2008新特性

    一.写本系列的目的 我平时利用课余零碎时间来学习ASP.NET3.5.LINQ.Silverlight.ASP.NET 3.5 Extensions等新东西,通过笔记形式来记录自己所学的历 程.也给大 ...

  3. Hackrank Equal DP

    Christy is interning at HackerRank. One day she has to distribute some chocolates to her colleagues. ...

  4. gdb条件断点

    1 通用格式 break file:line if condition condition的格式:变量名 条件运算符 常量,比如 if  i == 3 2 例 #include <stdio.h ...

  5. VisualSVN Server的配置和使用

    VisualSVN Server的配置与使用 本版本为VisualSVN Server 2.7.3版本-不同的版本可能在设置有不同的差异,但都大同小异 1.1启动界面 安装好 VisualSVN Se ...

  6. Map dependencies with code maps

    https://docs.microsoft.com/en-us/visualstudio/modeling/map-dependencies-across-your-solutions Instal ...

  7. java 泛型的理解与应用

    为什么使用泛型? 举个例子: public class GenericTest { public static void main(String[] args) { List list = new A ...

  8. Junit常用操作

    断言方法: public static void assertEquals(Object[] expected, Object[] actual) // 测试a是否等于b public static ...

  9. 【HNOI2004】 打鼹鼠

    [题目链接] 点击打开链接 [算法] 动态规划 f[i]表示上一次打了第i只鼹鼠,所能打死的最多的鼹鼠数量 [代码] #include<bits/stdc++.h> using names ...

  10. 稳定婚姻(tarjan)

    传送门 这道题一开始可能以为是二分图匹配……?不过后来发现和二分图没啥大关系. 简单分析之后发现,把夫妻之间连边(男性向女性连边),之后再将每对曾经是情侣的人连边(女性向男性连边),当然以上的方向可以 ...