美文网首页
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