热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC是目前主流的WebMVC框架之一。我们使用浏览器通过地址 http:ip:portcontextPathpath进行访问,SpringMVC是如何得知用户到底

SpringMVC是目前主流的Web MVC框架之一。

我们使用浏览器通过地址  http://ip:port/contextPath/path进行访问,SpringMVC是如何得知用户到底是访问哪个Controller中的方法,这期间到底发生了什么。

本文将分析SpringMVC是如何处理请求与Controller之间的映射关系的,让读者知道这个过程中到底发生了什么事情。

本文实际上是在上文基础上,深入分析

HandlerMapping里的
HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
该方法的具体实现,包括它如何找到对应的方法,以及如何把结果保存在map里,以便让请求转发到对应的handler上,同时也分析了handleradaptor具体做了什么事情。

源码分析

在分析源码之前,我们先了解一下几个东西。

1.这个过程中重要的接口和类。

HandlerMethod类:

Spring3.1版本之后引入的。 是一个封装了方法参数、方法注解,方法返回值等众多元素的类。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

它的子类InvocableHandlerMethod有两个重要的属性WebDataBinderFactory和HandlerMethodArgumentResolverComposite, 很明显是对请求进行处理的。

InvocableHandlerMethod的子类ServletInvocableHandlerMethod有个重要的属性HandlerMethodReturnValueHandlerComposite,很明显是对响应进行处理的。

ServletInvocableHandlerMethod这个类在HandlerAdapter对每个请求处理过程中,都会实例化一个出来(上面提到的属性由HandlerAdapter进行设置),分别对请求和返回进行处理。  (RequestMappingHandlerAdapter源码,实例化ServletInvocableHandlerMethod的时候分别set了上面提到的重要属性)

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

MethodParameter类:

HandlerMethod类中的parameters属性类型,是一个MethodParameter数组。MethodParameter是一个封装了方法参数具体信息的工具类,包括参数的的索引位置,类型,注解,参数名等信息。

HandlerMethod在实例化的时候,构造函数中会初始化这个数组,这时只初始化了部分数据,在HandlerAdapter对请求处理过程中会完善其他属性,之后交予合适的HandlerMethodArgumentResolver接口处理。

以类DeptController为例:

@Controller
@RequestMapping(value = "/dept")
public class DeptController {
  @Autowired
  private IDeptService deptService;
  @RequestMapping("/update")
  @ResponseBody
  public String update(Dept dept) {
    deptService.saveOrUpdate(dept);
    return "success";
  }
}

(刚初始化时的数据)

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

(HandlerAdapter处理后的数据)

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

RequestCondition接口:

Spring3.1版本之后引入的。 是SpringMVC的映射基础中的请求条件,可以进行combine, compareTo,getMatchingCondition操作。这个接口是映射匹配的关键接口,其中getMatchingCondition方法关乎是否能找到合适的映射。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

RequestMappingInfo类:

Spring3.1版本之后引入的。 是一个封装了各种请求映射条件并实现了RequestCondition接口的类。

有各种RequestCondition实现类属性,patternsCondition,methodsCondition,paramsCondition,headersCondition,consumesCondition以及producesCondition,这个请求条件看属性名也了解,分别代表http请求的路径模式、方法、参数、头部等信息。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

RequestMappingHandlerMapping类:

处理请求与HandlerMethod映射关系的一个类。

2.Web服务器启动的时候,SpringMVC到底做了什么。

先看AbstractHandlerMethodMapping的initHandlerMethods方法中。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

我们进入createRequestMappingInfo方法看下是如何构造RequestMappingInfo对象的。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

PatternsRequestCondition构造函数:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

类对应的RequestMappingInfo存在的话,跟方法对应的RequestMappingInfo进行combine操作。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后使用符合条件的method来注册各种HandlerMethod。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

下面我们来看下各种RequestCondition接口的实现类的combine操作。

PatternsRequestCondition:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

RequestMethodsRequestCondition:

方法的请求条件,用个set直接add即可。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

其他相关的RequestConditon实现类读者可自行查看源码。

最终,RequestMappingHandlerMapping中两个比较重要的属性

private final Map handlerMethods = new LinkedHashMap();

private final MultiValueMap urlMap = new LinkedMultiValueMap();

T为RequestMappingInfo。

构造完成。

我们知道,SpringMVC的分发器DispatcherServlet会根据浏览器的请求地址获得HandlerExecutionChain。

这个过程我们看是如何实现的。

首先看HandlerMethod的获得(直接看关键代码了):

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

这里的比较器是使用RequestMappingInfo的compareTo方法(RequestCondition接口定义的)。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后构造HandlerExecutionChain加上拦截器

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

实例

写了这么多,来点例子让我们验证一下吧。

@Controller
@RequestMapping(value = "/wildcard")
public class TestWildcardController {
  @RequestMapping("/test/**")
  @ResponseBody
  public String test1(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "TestWildcardController -> /test/**");
    return view;
  }
  @RequestMapping("/test/*")
  @ResponseBody
  public String test2(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "TestWildcardController -> /test*");
    return view;
  }
  @RequestMapping("test?")
  @ResponseBody
  public String test3(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "TestWildcardController -> test?");
    return view;
  }
  @RequestMapping("test/*")
  @ResponseBody
  public String test4(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "TestWildcardController -> test/*");
    return view;
  }
}

由于这里的每个pattern都带了*因此,都不会加入到urlMap中,但是handlerMethods还是有的。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

当我们访问: http://localhost:8888/SpringMVCDemo/wildcard/test1的时候。

会先根据 “/wildcard/test1” 找urlMap对应的RequestMappingInfo集合,找不到的话取handlerMethods集合中所有的key集合(也就是RequestMappingInfo集合)。

然后进行匹配,匹配根据RequestCondition的getMatchingCondition方法。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

最终匹配到2个RequestMappingInfo:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

然后会使用比较器进行排序。

之前也分析过,比较器是有优先级的。

我们看到,RequestMappingInfo除了pattern,其他属性都是一样的。

我们看下PatternsRequestCondition比较的逻辑:

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

因此,/test*的通配符比/test?的多,因此,最终选择了/test?

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

直接比较优先于通配符。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

@Controller
@RequestMapping(value = "/priority")
public class TestPriorityController {
  @RequestMapping(method = RequestMethod.GET)
  @ResponseBody
  public String test1(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "其他condition相同,带有method属性的优先级高");
    return view;
  }
  @RequestMapping()
  @ResponseBody
  public String test2(ModelAndView view) {
    view.setViewName("/test/test");
    view.addObject("attr", "其他condition相同,不带method属性的优先级高");
    return view;
  }
}

这里例子,其他requestCondition都一样,只有RequestMethodCondition不一样。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

看出,方法多的优先级越多。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

至于其他的RequestCondition,大家自行查看源码吧。

资源文件映射

以上分析均是基于Controller方法的映射(RequestMappingHandlerMapping)。

SpringMVC中还有静态文件的映射,SimpleUrlHandlerMapping。

DispatcherServlet找对应的HandlerExecutionChain的时候会遍历属性handlerMappings,这个一个实现了HandlerMapping接口的集合。

由于我们在*-dispatcher.xml中加入了以下配置:

Spring解析配置文件会使用ResourcesBeanDefinitionParser进行解析的时候,会实例化出SimpleUrlHandlerMapping。

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

其中注册的HandlerMethod为ResourceHttpRequestHandler。

访问地址: http://localhost:8888/SpringMVCDemo/static/js/jquery-1.11.0.js

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

SpringMVC源码分析4:DispatcherServlet如何找到正确的Controller

地址匹配到/static/**。

最终SimpleUrlHandlerMapping找到对应的Handler -> ResourceHttpRequestHandler。

ResourceHttpRequestHandler进行handleRequest的时候,直接输出资源文件的文本内容。

总结

大致上整理了一下SpringMVC对请求的处理,包括其中比较关键的类和接口,希望对读者有帮助。

让自己对SpringMVC有了更深入的认识,也为之后分析数据绑定,拦截器、HandlerAdapter等打下基础。




推荐阅读
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • 本文介绍如何使用 Angular 6 的 HttpClient 模块来获取 HTTP 响应头,包括代码示例和常见问题的解决方案。 ... [详细]
  • Struts与Spring框架的集成指南
    本文详细介绍了如何将Struts和Spring两个流行的Java Web开发框架进行整合,涵盖从环境配置到代码实现的具体步骤。 ... [详细]
  • 本文介绍如何将自定义项目设置为Tomcat的默认访问项目,使得通过IP地址访问时直接展示该自定义项目。提供了三种配置方法:修改项目路径、调整配置文件以及使用WAR包部署。 ... [详细]
  • 本文探讨了如何在iOS开发环境中,特别是在Xcode 6.1中,设置和应用自定义文本样式。我们将详细介绍实现方法,并提供一些实用的技巧。 ... [详细]
  • 解决FCKeditor应用主题后上传问题及优化配置
    本文介绍了在Freetextbox收费后选择FCKeditor作为替代方案时遇到的上传问题及其解决方案。通过调整配置文件和调试工具,最终解决了上传失败的问题,并对相关配置进行了优化。 ... [详细]
  • Python处理Word文档的高效技巧
    本文详细介绍了如何使用Python处理Word文档,涵盖从基础操作到高级功能的各种技巧。我们将探讨如何生成文档、定义样式、提取表格数据以及处理超链接和图片等内容。 ... [详细]
  • 本文介绍了如何利用 Spring Boot 和 Groovy 构建一个灵活且可扩展的动态计算引擎,以满足钱包应用中类似余额宝功能的推广需求。我们将探讨不同的设计方案,并最终选择最适合的技术栈来实现这一目标。 ... [详细]
  • 在项目部署后,Node.js 进程可能会遇到不可预见的错误并崩溃。为了及时通知开发人员进行问题排查,我们可以利用 nodemailer 插件来发送邮件提醒。本文将详细介绍如何配置和使用 nodemailer 实现这一功能。 ... [详细]
  • 深入理解 .NET 中的中间件
    中间件是插入到应用程序请求处理管道中的组件,用于处理传入的HTTP请求和响应。它在ASP.NET Core中扮演着至关重要的角色,能够灵活地扩展和自定义应用程序的行为。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 本文将详细探讨Linux pinctrl子系统的各个关键数据结构,帮助读者深入了解其内部机制。通过分析这些数据结构及其相互关系,我们将进一步理解pinctrl子系统的工作原理和设计思路。 ... [详细]
  • Ihaveastringwithquotesaroundthepathasfollows:我在路径周围有一个带引号的字符串,如下所示:C:\ProgramFiles(x ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
author-avatar
推广中医
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有