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 ...
随机推荐
- poj2488--A Knight's Journey(dfs,骑士问题)
A Knight's Journey Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 31147 Accepted: 10 ...
- JavaScript的高大强
1,JavaScript的引入方式 1.1>Script标签内写代码 <Script> //这里写JS代码的地方 </Script> 1.2>引入额外的JS文件 & ...
- centos6.5 yum安装MySQL5.6
创建MySQL用户 #useradd mysql #passwd mysql #chmod u+w /etc/sudoers #vi /etc/sudoers mysql ALL=(ALL) ALL ...
- 编程题:1. var person = '{name:"Lily",sex:"famale",age:24,country:"US"}';将person转换成JSON对象并便利每个属性值。
/// <summary> /// Json工具类 /// </summary> public class JsonUtility { private static JsonU ...
- YTU 1005: 渊子赛马
1005: 渊子赛马 时间限制: 1000 Sec 内存限制: 64 MB 提交: 338 解决: 49 题目描述 赛马是一古老的游戏,早在公元前四世纪的中国,处在诸侯割据的状态,历史上称为&qu ...
- 修改eclipse启动程序超时时间
修改workspace\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml 把其中的start-timeout="45&qu ...
- NSArray, NSSet, NSDictionary
一.Foundation framework中用于收集cocoa对象(NSObject对象)的三种集合分别是: NSArray 用于对象有序集合(数组)NSSet 用于对象无序集合(集合) NSDic ...
- UI:数据的解析
在懒加载的时候要注意事项: 必须使用 self.XX 的样式去开辟空间,不能使用 _XX 的格式去开辟,因为前者是内部的 set 方法,而后者并不走内部的 set 方法. json 文件的创建 xm ...
- C语言的随机发牌程序(红桃、黑桃、梅花、方块)
做一个随机发牌的C语言程序,供大家学习,思考. 未做任何注释,有测试时候留下的一些输出语句,一遍方便测试. /* author:nunu qq:398269786 */ #include<std ...
- 线上项目mysql、redis平滑迁移方案及步骤
1.清晰系统内网及公网可达,CVM配置 2.迁移完整数据,项目部署,测试网络环境. redis:复制rdb文件mysql:xtrabackup备份3.确保项目正常运行,网络正常访问.项目对外接口及账户 ...