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

news2024/11/17 13:31:35

前面分析了 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 接口来进行处理的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/463607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 二进制 八进制 十进制 十六进制之间的转换

众所周知&#xff1a;计算机底层是以二进制数来进行存储计算&#xff0c;而计算机进制&#xff1a;数制是用一组固定的符号和统一的规则来表示数值的方法。 开始下面讲述之前首先要声明&#xff1a; 二进制&#xff0c;八进制&#xff0c;十六进制 都可以转换为十进制&#xf…

【DRF配置管理】如何在视图类使用get_objects()

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 DRF应用和管理 【DRF配置管理】Django使用DRF框架 【DRF配置管理】如何在视图类配置参数(一) 【DRF配置管理】如何在视图类配置参数(二) 【DRF配置管理…

第二届广州·琶洲算法大赛启动,百度飞桨助力广州打造中国算法新高地

‍‍ 生成式人工智能热潮席卷全球&#xff0c;算法创新成为 AI 突破发展的关键&#xff0c;推动实体经济高质量增长。4月25日&#xff0c;第二届广州琶洲算法大赛正式启动&#xff0c;广州市政府主办、百度飞桨等联合承办&#xff0c;广召天下算法英雄&#xff0c;加快“琶洲算…

<网络编程>网络套接字

目录 理解源IP地址和目的IP地址 认识端口号 端口号和进程ID的关系 理解源端口号和目的端口号 初步认识TCP、UDP协议 TCP协议 UDP协议 网络字节序列 socket网络接口 socket常见API sockaddr结构 UDPsocket 编码&#xff1a; 理解源IP地址和目的IP地址 源IP&#xf…

服装店铺装修有哪些窍门?做好这3点,顾客主动上门

现在街边有各种各样的服装店&#xff0c;有的服装店客流不断&#xff0c;有的服装店却很冷清&#xff0c;导致这种现象的原因有很多&#xff0c;比较重要的一点就是你的服装店铺装修没做好。 你的服装店铺装修足够吸引人吗&#xff1f; 什么样的服装店铺装修才能吸引顾客&#…

【技巧】如何修改PDF文件?

PDF文件格式安全、标准化&#xff0c;很多人在工作中几乎离不开。可有些小伙伴想要修改PDF文件内容时&#xff0c;发现无法修改&#xff0c;那是什么情况呢&#xff1f;如何才能修改PDF文件呢&#xff1f;下面小编就来分享一些小技巧。 技巧一&#xff1a;使用PDF编辑器 如果使…

凌恩生物文献分享|一株细菌完成图也能发一区10分+!

期刊&#xff1a;Science of the Total Environment 影响因子&#xff1a;10.753 发表时间&#xff1a;2022 样本类型&#xff1a;Bosea sp. Ads-6菌株 客户单位&#xff1a;中国科学院微生物研究所 一、研究背景 环境中抗生素残留和耐药性的增加引发了许多…

一文详解汽车操作系统现状

摘要&#xff1a; 智能座舱和自动驾驶的发展&#xff0c;特斯拉的突飞猛进&#xff0c;让各大主机厂越来越重视汽车操作系统。但车企现在所做的软件定义汽车&#xff0c;大都是通过软硬件解耦来降低造车成本、丰富新车功能&#xff0c;在操作系统层面大都还停留在市场调研和学…

linux ubantu 16.04 安装fbprophet 和 pystan经验总结

写在前面 之前在window11上&#xff0c;安装了一下午&#xff0c;不是C版本不行&#xff0c;就是这个那个不通过&#xff0c;主要是Pystan运行不起来就很气&#xff0c;fbprophet本身就需要依赖这个包&#xff0c;然后MSVC不支持&#xff0c;裂开。尝试了很多次&#xff0c;碰…

基于Java开发的分布式在线教育系统,支持考试、直播、问答

一、开源项目简介 知道学习平台是一个基于 Java 开发的分布式在线教育系统项目采用前后端分离的企业级微服务架构引入组件化的思想实现高内聚低耦合&#xff0c;项目代码简洁注释丰富上手容易注重代码规范&#xff0c;严格控制包依赖可以帮助个人、企业或机构快速搭建一个在线…

C# 利用TabControl控件制作多窗口切换

TabControl控件切换时触发的事件 选项卡切换触发的是TabControl控件的SelectedIndexChanged事件。 当TabControl控件的任何一个TabPage被点击或选择&#xff0c;即发生SelectedIndexChanged事件事件。 代码如下&#xff1a; private void tabControl1_SelectedIndexChanged(o…

设计模式 --- 结构型模式

一、概述 结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式&#xff0c;前者采用继承机制来组织接口和类&#xff0c;后者釆用组合或聚合来组合对象。 由于组合关系或聚合关系比继承关系耦合度低&#xff0c;满足“合成复用原则”…

L2TP Client-initated场景

L2TP Client-initated场景 1. 原理 ![原理](https://img-blog.csdnimg.cn/66ce3169502b4252bca5d9d7a6c0027c.png)1.1 阶段1&#xff1a;创建L2TP隧道 C与LNS通过交互三条消息协商隧道ID、UDP端口&#xff08;1701&#xff09;、主机名称、L2TP版本、隧道验证等参数。 1.2 …

使用Spark实现词频统计

文章目录 一&#xff0c;词频统计准备工作&#xff08;一&#xff09;版本选择问题&#xff08;二&#xff09;安装Scala2.12.15&#xff08;三&#xff09;启动集群的HDFS与Spark&#xff08;四&#xff09;在HDFS上准备单词文件 二&#xff0c;本地模式运行Spark项目&#xf…

Meta的分割一切模型SAM( Segment Anything )测试

Meta不久前开源发布了一款图像处理模型&#xff0c;即分割一切模型&#xff1a;Segment Anything Model&#xff0c;简称 SAM&#xff0c;号称要从任意一张图片中分割万物&#xff0c;源码地址为&#xff1a; https://github.com/facebookresearch/segment-anything打开后看到…

ssm+java企业公司产品分销商管理系统

一、 二、经营管理&#xff1a; ①分销商每月提交自己进多少货物&#xff08;从总部进购了多少“鹊巢”的商品给自己负责区的大型商超&#xff09;——对应的种类一共进多少货物&#xff1b;该种类中具体的产品又进了多少货物具体到&#xff08;参考三产品管理模块&#xff09;…

RelativeLayout相对布局

一、官方地址&#xff1a; https://developer.android.google.cn/reference/kotlin/android/widget/RelativeLayout?hlen 二、概述 相对布局&#xff08;RelativeLayout&#xff09;是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式 三、基本格式 <RelativeLay…

Nacos注册中心的使用

文章目录 Nacos注册中心1. 服务注册到nacos1&#xff09;引入依赖2&#xff09;配置nacos地址3&#xff09;重启 2.服务分级存储模型2.1.给user-service配置集群2.2.同集群优先的负载均衡 3.权重配置 Nacos注册中心 国内公司一般都推崇阿里巴巴的技术&#xff0c;比如注册中心…

Docker安装Redis(普通安装+在线安装+离线安装)

文章目录 Redis概述一、磁盘安装1.1 安装环境1.2 安装步骤1.3 服务器启停命令 二、docker安装1.在线安装2.离线安装 总结 Redis概述 Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支…

ext-1:PDK工具包编译出例程

1、TI的单独StarterWare不更新后&#xff0c;后续维护和更新的是 PROCESSOR-SDK-AM335X 软件开发套件 &#xff08;PDK&#xff09;&#xff0c;对比以前的&#xff0c;里面没有例程&#xff0c;所以下载安装完需要自己编译出example例程。 因为编译出example例程中间会出现很…