首页 > 其他分享 >【老王读SpringMVC-3】根据 url 是如何找到 controller method 的?

【老王读SpringMVC-3】根据 url 是如何找到 controller method 的?

时间:2023-02-14 16:03:25浏览次数:43  
标签:匹配 映射 获取 SpringMVC request url controller handler method

前面分析了 request 与 handler method 映射关系的注册,现在再来分析一下 SpringMVC 是如何根据 request 来获取对应的 handler method 的?

可能有人会说,既然已经将 request 与 handler method 映射关系注册保存在了 AbstractHandlerMethodMapping.MappingRegistry#registry 中,那么根据 request 不就能直接从 registry 中获取到相应的 handler method 了吗?

如果我们定义的 Controller 中的 @RequestMapping 都是普通的字符的话,那确实是可以直接通过 registry 获取 handler method。
但是,@RequestMapping 还支持占位符、通配符等 url,例如:

@RequestMapping("/resources/ima?e.png") // 匹配路径段中的一个字符
@RequestMapping("/resources/*.png") // 匹配路径段中的零个或多个字符
@RequestMapping("/resources/**") // 匹配多个路径段
@RequestMapping("/projects/{project}/versions") // 匹配路径段并将其捕获为变量
@RequestMapping("/projects/{project:[a-z]+}/versions") // 使用正则表达式匹配并捕获变量

所以,查找 request 对应的 handler method 就不是那么简单的事情了。

如何通过 request 获取 handler?

通过对 DispatcherServlet 的分析,我们知道 SpringMVC 获取 handler 的方法如下:
org.springframework.web.servlet.DispatcherServlet#getHandler()

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

可以看出,SpringMVC 是通过 HandlerMapping#getHandler() 来获取 request 对应的 handler method 处理程序的,最终会拿到一个 HandlerExecutionChain

HandlerExecutionChain 中包含了 request 对应的 handler method 和 interceptors。

HandlerMapping 的类图如下: HandlerMapping.png

HandlerMapping 的实现类中其中最常用的是 RequestMappingHandlerMapping,它是专门用来处理 @RequestMapping 定义的 request 请求映射的。

获取 HandlerExecutionChain 的过程

HandlerExecutionChain 的获取是在 AbstractHandlerMapping#getHandler() 中完成的。
主要分成了两步:
1、获取 request 对应的 handler 程序
2、将 handler 和 相应的 HandlerInterceptors 组装成 HandlerExecutionChain

getHandler.png

获取 request 对应的 handler method 的过程

上一步获取 HandlerExecutionChain 时,会调用 AbstractHandlerMethodMapping#getHandlerInternal() 来获取 request 对应的 handler method。
最终它会调用到 AbstractHandlerMethodMapping#lookupHandlerMethod() 来获取。

lookupHandlerMethod.png

可以看到,AbstractHandlerMethodMapping#lookupHandlerMethod 实现了 request 与 handler method 映射关系的查找:
1、首先,通过 directPath 直接获取映射的 handler method
2、如果通过 directPath 没有找到的话,就循环 registry 中所有的映射,查出映射关系
3、如果获取到的映射的 handler 个数 > 1 的话,就找出最合适的匹配

DirectPath: 非 patterns 的 path。
非 patterns 的 path 是指 path 的定义中没有 ?、*、{} 的 path

通过 lookupPath 获取 HandlerMethod

AbstractHandlerMethodMapping#lookupHandlerMethod() 在寻找 HandlerMethod 时,有两种逻辑:

  • 1、直接通过 directPath 快速查找
  • 2、循环所有的映射关系,查找 RequestCondition 条件匹配,获取最合适的匹配

直接通过 directPath 快速查找

directPath 是最快速的方式,直接从 map 中获取匹配结果。
directPath 是不包含占位符、模式匹配符的普通路径,所以这种普通路径是最直接,也是效率最高的。

循环所有的映射关系,通过 RequestCondition 条件匹配,获取最合适的匹配

如果通过 directPath 没有找到任何匹配的话,就需要遍历所有注册的映射关系,找出最合适的匹配。


addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);

// 这里会循环所有注册的映射关系,将满足 RequestCondition 的映射查找出来,存放到 matches 中
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
        }
    }
}

对于 @RequestMapping 注解形式的 url 映射,AbstractHandlerMethodMapping#getMatchingMapping() 最终会调用实现类的方法 RequestMappingInfoHandlerMapping#getMatchingCondition() 来获取匹配。

getMatchingCondition.png

可以看到,Spring 会检查 RequestMappingInfo 是否满足以下 8 个条件:
1、请求方法 RequestMethod 是否匹配,如:GET, POST, PUT, DELETE 等
2、请求参数 params 是否匹配
3、请求头 header 是否匹配
4、consumes Content-Type 是否匹配
5、produces Content-Type 是否匹配
6、处理 directPath 和 通配符的请求映射匹配
7、处理 ant 风格的请求映射匹配
8、处理自定义的请求映射条件匹配

关于 RequestCondition 的详细分析查看 @RequstMapping和RequestCondition

小结

SpringMVC 是通过 HandlerMapping#getHandler() 来获取 request 对应的 handler method 处理程序的。
底层实现是通过 AbstractHandlerMethodMapping#lookupHandlerMethod 来查找 request 对应的 handler method:
1、首先,通过 directPath 直接获取映射的 handler method
2、如果通过 directPath 没有找到的话,就循环 registry 中所有的映射,查出映射关系
3、如果获取到的映射的 handler 个数 > 1 的话,就找出最合适的匹配

对于 url 映射中的通配符、ant 风格的请求,都是通过 RequestCondition 接口来进行处理的。

标签:匹配,映射,获取,SpringMVC,request,url,controller,handler,method
From: https://blog.51cto.com/laowang6/6056382

相关文章

  • webpack4基本使用和url-loader压缩图片
    执行npminit-y,创建package.json文件最基本的配置文件entry:入口文件output:出口,出口里面添地址,和导出的包的名字mode是模式默认production,可以选择为development,也......
  • SpringMVC:域对象共享数据
    SpringMVC:域对象共享数据使用ServletAPI向request域对象共享数据@RequestMapping("/testServletAPI")publicStringtestServletAPI(HttpServletRequestrequest){......
  • Java 通过get post 请求url
    .已获取小程序的access_token为例,通过Get请求url1importcom.alibaba.fastjson.JSONObject;23StringwechatUrl="https://api.weixin.qq.com/cgi-bin/tok......
  • day08-SpringMVC底层机制简单实现-04
    SpringMVC底层机制简单实现-04https://github.com/liyuelian/springmvc-demo.git8.任务7-完成简单视图解析功能说明:通过目标方法返回的String,转发或重定向到指定页面......
  • 七、SpringMVC异常处理机制
    异常处理的思路系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生......
  • 位图:爬虫URL去重最佳方案
    网页爬虫,解析已爬取页面中的网页链接,再爬取这些链接对应网页。而同一网页链接有可能被包含在多个页面中,这就会导致爬虫在爬取的过程中,重复爬取相同的网页。1如何避免重复爬......
  • The Protocol-relative URL|协议相对网址
    前言最近基于https的网站,引用http网站的资源文件出现了报错,最初的解决办法是添加两套引入,也就是http、https都写上<scriptsrc="http://a.js"></script><script......
  • springmvc文件上传(ajax请求 带参数)
    前言这里用ajax文件上传,并携带几个参数,网上查到的大多都是没带参数只有文件的。由于我项目代码太多,这里只给出关键代码。操作我用的SSM框架,传之前,先在WEB-INF/dispatcherSe......
  • 解决curl中文乱码问题
    文章目录​​1、问题描述​​​​2、解决方案:安装iconv​​1、问题描述curl下载地址:​​https://curl.se/download.html​​​在执行命令​​curlwww.baidu.com​​的时候......
  • SpringMVC工作流程
    1前端控制器dispatcherServlet接收到用户请求2dispathcherServlet调用处理器映射器handlerMapping3handlerMapping根据url找到对应处理器返回给dispatcherServlet(可......