Spring是如何找到URL请求对应的Controller的

2024-09-07 00:18

本文主要是介绍Spring是如何找到URL请求对应的Controller的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章来源

原文作者:Spring MVC 原文地址:
https://blog.csdn.net/hl233211/article/details/77450697
http://ddrv.cn/a/58528
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。

序:先贴一张SpringMVC整体的框架原理图
在这里插入图片描述
此文主要描述Spring在响应请求的时候是如何根据URL找到对应的处理方法(Handler Method)的。即上图中的第二步(版本号:4.3.4.RELEASE)。

读源码最好的方法是一边调试一边理解,直接靠读很可能最后的结果就是能看懂每行甚至每个方法的意思,但是一串联起来就完全懵圈了,至少我是这个样子的。

1,那么既然要调试,很明显就需要找调试的入口,阅读的起点就在请求分发的地方:DispatcherServlet.doDispatch

此处先大概过一下这个方法,立马我们可以理解根据请求获取对应的处理器主要就在这个方法:getHandler(processedRequest)
在这里插入图片描述
对应的方法详细实现:
在这里插入图片描述
这里可以发现,它的逻辑很简单,就是循环内存中的handlerMappings,请求每个handleMapping返回一个HandlerExecutionChain。这个HandlerExecutionChain主要是包括了一个handler(处理对象,Object类型),以及一个拦截器列表(拦截器非本次研究对象,先无视)。

OK,那代码阅读至此,我们第一个疑问是:

Q1:DispatcherServlet中的handlerMappings是什么,它是哪里来的?

2,然后我们继续看,下面的核心代码是hm.getHandler(request)方法,它被AbstractHandlerMapping这个抽象类实现。
在这里插入图片描述
它直接调用了自身定一个的一个抽象方法:getHandlerInternal,将返回的结果组装成HandlerExecutionChain类型。

AbstractHandlerMethodMapping这个class实现了这个抽象方法
在这里插入图片描述
该方法先根据request获取到请求的资源路劲(lookupPath),然后调用lookupHandlerMethod方法。
在这里插入图片描述
这边代码稍微有点多,但是还是可以理解。它定义了一个matchs的列表,然后内存变量mappingRegistry根据lookupPath获取所有的匹配对象。

如果存在这个匹配对象,那么回调用addMatchingMappings方法将匹配的对象映射到matches上。

如果未匹配到这个lookupPath,那么则会循环mappingRegistry中每个Mapping来匹配该路劲(因为RequestMapping中定义的路劲可以有通配符)。

getMappingsByUrl方法:

在这里插入图片描述
此处又增加一个疑问:

Q2:这个mappingRegistry变量是何物,什么时候将Mapping结果保存的?

那么现在带着Q1和Q2,我们只需要再了解这个方法了:addMatchingMappings,至于后面的代码,只是看看有没有一个请求映射到多个处理方法,如果存在并且优先级还一样,那么就会报错:Ambiguous handler methods mapped for HTTP path。

3,继续往下,以下为addMatchingMappings方法的逻辑:

在这里插入图片描述
注意到AbstractHandlerMethodMapping这个class是个泛型抽象类 getMatchingMapping是个抽象方法,返回值为定义的泛型(T),我们暂且把这个泛型成为[映射信息]。

根据方法名称,T getMatchingMapping(T,reqeust) ,根据映射信息和请求获取是否匹配,如果匹配则返回映射信息。

RequestMappingInfoHandlerMapping这个class继承了这个抽象类,泛型类型为:RequestMappingInfo。看看它的方法实现:

在这里插入图片描述
在这里插入图片描述
前五行根据字面意思是分别根据请求判断各要素是否匹配。后两者其实也是,逻辑大部分都一样。

至于为什么源码立马会拆开写,可能是后两者的match方法更加稍微耗时一点吧。

这里7个要素如果都匹配了的话,那么可以说这个[映射信息]就匹配成功了。(其中前六个为自带属性,最后一个为自定义属性)
7要素分别为:

  1. methods : 请求方法匹配(POST/GET/DELETE/PUT等等),对应RequestMapping注解的method
  2. params : 请求参数匹配 例如myParam=myValue, 对应RequestMapping注解的params
  3. headers:请求头信息(例如:My-Header!=myValue),对应RequestMapping的headers
  4. consumes:提交内容类型(例如:application/json,text/html;),对应RequestMapping的consumes
  5. produces:返回的内容类型(例如:application/json),对应RequestMapping的consumes
  6. patterns:URL格式(例如:test/testDo),对应RequestMapping的value
  7. 自定义条件,后续会针对它再写一篇博文。

这些要素的匹配逻辑比较简单,无非就是把请求中的属性和处理方法ReqeustMapping中的属性值比对是否匹配,此处不再做深入。

Q1:DispatcherServlet中的handlerMappings是什么,它是哪里来的?

DispatcherService的initHandlerMappings中初始化了这个成员变量。
在这里插入图片描述
逻辑无非就是从spring仓库中获取实现了HandleMapping接口的组件。

以我们常用的配置:annotation-driven为例,其对应的处理解析类为AnnotationDrivenBeanDefinitionParser,它的parse方法:
在这里插入图片描述
可以看到,它注册了RequestMappingHandlerMapping这个实现类,而RequestMappingHandlerMapping继承了RequestMappingInfoHandlerMapping

再如通过配置注解的WebMvcConfigurationSupport中,也将RequestMappingHandlerMapping注册在仓库中。
在这里插入图片描述

Q2:AbstractHandlerMethodMapping中的mappingRegistry变量是何物,什么时候将Mapping结果保存的?

主要的成员变量:
在这里插入图片描述
在AbstractHandlerMethodMapping中有个registerHandlerMethod方法,在初始化之后(afterPropertiesSet)对调用该方法

afterPropertiesSet -> initHandlerMethods ->detectHandlerMethods-> registerHandlerMethod
在这里插入图片描述
该方法的逻辑比较好理解,循环一个handler的所有内部方法,获取它的所有方法对应的匹配信息,并且将匹配信息保存在mappingRegistry中。

这里有个需要注意的是 getMappingForMethod 这个抽象方法就是抽取一个Handler里面Method对应的映射信息。

RequestMappingHandlerMapping的实现为:
在这里插入图片描述
先获取method对应的RequestMappingInfo,然后获取类对应的RequestMappingInfo(应为@RequestMapping也可以作为类的注解)最后两者进行combine。

combine逻辑即合并两个RequestMapping,比如类对应的path为 test,Method对应的path为 test1,那么最后合并的path即为 test/test1。

在这里插入图片描述
以下为Spring默认HandleMapping的类图:
在这里插入图片描述
好了,上述都是文字型的东西,下面匹配运行时数据强化记忆吧。

这篇关于Spring是如何找到URL请求对应的Controller的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143523

相关文章

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

Java异常架构Exception(异常)详解

《Java异常架构Exception(异常)详解》:本文主要介绍Java异常架构Exception(异常),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. Exception 类的概述Exception的分类2. 受检异常(Checked Exception)

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

springboot报错Invalid bound statement (not found)的解决

《springboot报错Invalidboundstatement(notfound)的解决》本文主要介绍了springboot报错Invalidboundstatement(not... 目录一. 问题描述二.解决问题三. 添加配置项 四.其他的解决方案4.1 Mapper 接口与 XML 文件不匹配

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

JavaScript错误处理避坑指南

《JavaScript错误处理避坑指南》JavaScript错误处理是编程过程中不可避免的部分,它涉及到识别、捕获和响应代码运行时可能出现的问题,本文将详细给大家介绍一下JavaScript错误处理的... 目录一、错误类型:三大“杀手”与应对策略1. 语法错误(SyntaxError)2. 运行时错误(R

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

JavaScript Array.from及其相关用法详解(示例演示)

《JavaScriptArray.from及其相关用法详解(示例演示)》Array.from方法是ES6引入的一个静态方法,用于从类数组对象或可迭代对象创建一个新的数组实例,本文将详细介绍Array... 目录一、Array.from 方法概述1. 方法介绍2. 示例演示二、结合实际场景的使用1. 初始化二