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 ...
随机推荐
- BestCoder Round #2 1001 TIANKENG’s restaurant
不得不说,bastcoder是个hack游戏啊.!. 题意:求最少要多少张椅子才干让全部来的客人有地方坐!! 就是一个区间的处理吧!!!和HDU 1556 我待水似流年.流年待我似水神似! ..! ...
- 【iOS系列】-单例模式的实现
1:重写allocWithZone方法 allocWithZone方法是对象分配内存空间时, alloc方法最终会调用这个方法 + (id)allocWithZone:(struct _NSZone ...
- debug找到source lookup path以及,debug跑到另外的解决办法
在我们使用eclipse调试的时候,有时候会出一些奇葩的问题,比如找不到Source lookup path, 这时我们可以点击Edit Source Lookup Path.接着回弹出一个 我们只 ...
- Cron Expression
CronTrigger CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表.CronTr ...
- mongo12---手动预先分片
手动预先分片:(每个片上的数据是不一样的,是分开存,不是做备份) 自动分片有可能短期内某个片的数据过大,硬盘不够用了.能否100000-30000就到1号片. //以shop.user表为例,先声明s ...
- How to Execute Page_Load() in Page's Base Class?
https://stackoverflow.com/questions/2737092/how-to-execute-page-load-in-pages-base-class We faced th ...
- inserting a large number of records with SQLiteStatement.
Below is a demo application I wrote that creates 100 records programmatically, inserts them using on ...
- java代码实现JDBC连接MySql以及引用驱动程序包
JDBC链接MySql JDBC链接MySql的话题已经老掉牙了,这次我只想通过使用简洁的代码实现,采用封装的思想,将链接MySql的代码封装在类的静态方法中,供一次性调用返回java.sql ...
- bzoj2002 [Hnoi2010]Bounce 弹飞绵羊——分块
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2002 第一次用分块,感觉超方便啊: 如果记录每个点的弹力系数,那么是O(1)修改O(n)查询 ...
- 浅谈JAVA中如何利用socket进行网络编程(二)
转自:http://developer.51cto.com/art/201106/268386.htm Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以 ...