美文网首页
RequestMapping执行过程- 3

RequestMapping执行过程- 3

作者: 程序员札记 | 来源:发表于2023-04-18 12:55 被阅读0次

我们已经介绍了 HandlerMethod 是如何被 自动扫描 出来,并注册到 RequestMappingHandlerMapping 中去的。

本文将延续上一篇,探究当一个请求 HttpServletRequest 到来,SpringMVC 又是如何 匹配获取 到合适的 HandlerMethod 的?

getHandler 时序图

image.png

在这张时序图中,最重要的一段方法就是 lookupHandlerMethod。接下来,对该方法逐段进行分析。

1.getMappingByUrl

getMappingByUrl 是在 lookupHandlerMethod 中调用的第一个方法。

方法功能:根据 url 路径,获取可能与之匹配的 RequestMappingInfo 列表

源码:

image.png

urlLookup 的实例是 LinkedMultiValueMap:

image.png

LinkedMultiValueMap 是 LinkedHashMap + LinkedList 的组合数据结构。

向 LinkedMultiValueMap 添加的地方也仅有一处:

源码

image.png

在注册时,需要调用 getDirectUrls 从 ReuqestMappingInfo 对象中提取作为索引 key 的 url 字符串。

1.1.getDirectUrls

源码

image.png

getDirectUrls 里面有两个让人在意的点:

  1. 一个是 getPathMatcher() 返回一个什么样的对象?

  2. 另一个是 getMappingPathPatterns 得到了怎样的集合?

1.2.AntPathMatcher

首先回答第一个问题:getPathMatcher() 返回一个什么样的对象?

答案:getPathMatcher() 默认返回的是 AntPathMatcher。

尽管可以自定义 PathMatcher 接口,但是 AntPathMatcher 还是比较常用的。Apache Ant 的官方指南 here

AntPathMatcher 实现的是 Ant 风格的路径匹配(Ant-style path patterns),遵循的 URL 匹配规则如下:

  1. ? 匹配一个字符

  2. * 匹配 >= 0 个字符

  3. ** 匹配 >= 0 个目录

  4. 特别地,{url:[a-z]+} 表示路径变量 url 符合正则表达式 [a-z]+

isPattern 源码:

image.png

看到 isPattern 这段代码,我们就可以明白 getDirectUrls 希望获得 <mark style="margin: 0px; padding: 0px; box-sizing: border-box;">不含通配符 的 url</mark>,这也就是 direct url 的第一层含义。

1.3.getMappingPathPatterns

接着回答第二个问题:getMappingPathPatterns 得到了怎样的集合?

答案:返回 Controller 类上作用于类的 @RequestMapping 注解和 Controller 类中方法上的作用于方法的 @RequestMapping 注解的结合。

追问1:为什么 RequestMapping 既可以是作用于类的注解,又可以是作用于方法的注解?

image.png

奥秘就在于 RequestMapping 注解类上面 @Target 注解的作用目标的声明。

追问2:为什么 getMappingPathPatterns() 能获取的是一个集合,而不是单个 url 呢?

答案:因为 @RequestMapping 的 value (别名 path)是 String[]

因此像这样的组合写法也是合法的。

@Controller
@RequestMapping({"/user", "/customer"})
public class UserController {

    @RequestMapping({"/info","/detail"})
    public ModelAndView user(String name, int age) {
        System.out.println("name=" + name);
        System.out.println("age=" + age);
        return null;
    }
}

此时,可以匹配的 url 有 4 个:

image.png

追问3:isPattern 没有排除 ${pathVariable} 的写法,那是不是表示 PathVariable 也算是 direct url 呢?

答案: PathVariable 也算是 direct url。

我们稍稍改变一下 UserController:

@Controller
@RequestMapping({"/user", "/customer"})
public class UserController {

    @RequestMapping("${name}")
    public ModelAndView user(@PathVariable String name, int age) {
        System.out.println("name=" + name);
        System.out.println("age=" + age);
        return null;
    }
}

这种情况下,getDirectUrls 就会返回 ["/user/${name}", "/customer/${name}"] 字符串数组。

2.addMatchingMappings

我们知道 url 仅仅是一个索引,假如我们调用 getDirectUrls 能获取到结果,就意味着“命中索引”,效率就会高很多。

否则就只能“全表搜索”了,效率就会低很多。

来看一下源码:

image.png

但是,无论是哪种方式,都会调用 addMatchingMappings。时序图如下:

image.png

addMatchingMappings 方法中,foreach 遍历第一个参数——— RequestMappingInfo 集合 mappings

  • getMatchingMapping:如果 RequestMappingInfo 和 HttpServletRequest 可以匹配,就返回一个新的 RequestMappingInfo对象,否则返回 null

  • getMatchingCondition:对 method,参数,请求头,consumes,produces,url patterns 等进行逐个匹配,假如有不匹配的,就返回 null。如果都能通过条件匹配,就返回一个新的 RequestMappingInfo对象。

getMatchingCondition 匹配规则比较复杂,涉及的类也还有很多,就不在本文展开了。

addMatchingMappings 方法中,每找到一个与 HttpServletRequest 匹配的 RequestMappingInfo,就会向参数 List<Match> matches 中加入一个 Match

Match 是由 RequestMappingInfo 以及对应的 HandlerMethod 组成。

3.对 Match 列表排序

现在继续回到 lookupHandlerMethod 方法。

既然,现在符合条件的 List<Match> matches 已经全部找出,我们就要排序并且筛选出最符合条件的 Match 了。

其中,最佳匹配对象 Match,它的 HandlerMethod 就是最佳 HandlerMethod。

总结

对于 RequestMappingHandlerMapping 而言,getHandler 就是要找一个最最匹配的 HandlerMethod 对象。

这个寻找最佳匹配 HandlerMethod 的逻辑就“藏”在 lookupHandlerMethod 中,主要步骤如下:

  • 第一步,用请求的 url 路径获取 RequestMappingInfo 列表(“url索引匹配”);若匹配不上,只能全量遍历所有 RequestMappingInfo。

  • 第二步,完全匹配 RequestMappingInfo 和 HttpServletRequest。匹配逻辑在 RequestMappingInfo#getMatchingCondition 中。

  • 第三步,对匹配结果 Match 排序,选出最佳的 HandlerMethod,排序比较的逻辑在 RequestMappingInfo#compareTo 中。

相关文章

网友评论

      本文标题:RequestMapping执行过程- 3

      本文链接:https://www.haomeiwen.com/subject/rbbhjdtx.html