关于springboot的Rest请求映射处理的源码分析(二)

news2024/9/28 5:31:12

前面我们知道了他怎么处理表单映射,这里我们来研究一下,他是如何处理具体请求的。也就是说我有那么多/user你是怎么定位到我在哪个cotroller,并且你是怎么定位到我具体是哪个接口。
这里我们就来逐步定位一下这个问题。

一、组件分析

老路子,我们的所有逻辑都是一个个组件串起来的,我们先把组件一个个拿出来。看看他的功能。

组件1、@RequestMapping

我们的请求接口都标注了这个注解,然后生效的,虽然没啥逻辑,但是还是列出来。

组件2、请求处理器RequestMappingHandlerMapping

我们看到他在WebMvcAutoConfiguration中注册了一堆bean,是用来做请求映射处理的,其中有一个名字就是RequestMappingHandlerMapping 一看就是用来处理我们的@RequestMapping注解请求的。其余还有什么WelcomePageHandlerMapping 是用来处理欢迎页的,也就是那个默认的index.html页面的跳转。我们不管这个,就看到他给了RequestMappingHandlerMapping。

@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
	// Must be @Primary for MvcUriComponentsBuilder to work
	return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
			resourceUrlProvider);
}

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,
		FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
	WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(
			new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),
			this.mvcProperties.getStaticPathPattern());
	welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
	welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());
	return welcomePageHandlerMapping;
}

貌似都有了,有了映射,有了映射处理器,此外的就是看逻辑了。

二、源码流程

1、找到入口

我们先明确一下,怎么分析mvc这类源码,我们都知道入口类就是DispatcherServlet。而他再怎么花哨也就是个servlet,既然你是servlet,那就肯定主要逻辑实现在那个叫做doGet和doPost的方法里面。
我们就去找这个方法,可能这个里面没有,但是父类里面绝壁有。我们最后在FrameworkServlet这个类里面找到了这两个方法。

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}

/**
 * Delegate POST requests to {@link #processRequest}.
 * @see #doService
 */
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	processRequest(request, response);
}

	......还有什么doDelete之类的不说了。都一样

我们看到他其实是都走了processRequest(request, response);这个方法。最后我们一路翻他的主要实现逻辑依次是processRequest->org.springframework.web.servlet.DispatcherServlet#doService->
org.springframework.web.servlet.DispatcherServlet#doDispatch 这就是我们每次定位MVC问题的那个入口类。就是这么分析来的。
于是我们来看这个类的这个方法。

2、主要逻辑

org.springframework.web.servlet.DispatcherServlet#doDispatch
一些没用的代码我就删了,我们这里主要关注请求映射处理。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        
	HttpServletRequest processedRequest = request;                                                            
	// 删除没用的                                
                                                                                                              
	try {                                                                                                     
		ModelAndView mv = null;                                                                               
		Exception dispatchException = null;                                                                   
                                                                                                              
		try {  
			// 看是不是文件上传请求,我们这里不关注                                                                                               
			processedRequest = checkMultipart(request);                                                       
			multipartRequestParsed = (processedRequest != request);                                           
                                                                                                              
			// Determine handler for the current request.  
			// 看着源码注释也知道他是决定你哪个处理器来处理你的请求的,其实就是这里                                                   
			mappedHandler = getHandler(processedRequest);                                                     
			
			// 后续就是走具体逻辑了,我们只关注他怎么找到请求执行的。后面不关注                                 
		}                                                                                                     
		                                        
}                                                                                                             

所以我们看到主要逻辑落在了mappedHandler = getHandler(processedRequest);这个方法上面,
我们就来看看这个方法。
org.springframework.web.servlet.DispatcherServlet#getHandler

@Nullable                                                                                    
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;                                                                             
}                                                                                            

我们看到他遍历了一个handlerMapping的集合,然后看哪个能处理你的这个请求,就直接返回了,我们这里需要debug一下,看看这个集合里面都有啥。
我们把断点打到这里。然后调用一个GET请求。
在这里插入图片描述
在这里插入图片描述
我们看到他其实就是我们组件2的集合,其中第一个就是我们的组件2,而且他里面加载了所有的标注了@ReauestMapping的请求路径的集合,他在初始化加载的时候就封装进去了。于是我们知道他在组件2里面就有了我们所有的请求路径的类,接口,以及其他信息,都封装了。
封装结构为map,其中key就是请求路径,比如/user
value就是你这个路径哪个controller,哪个方法之类的。这样就能一下就找到了。
我们来直接进入这个方法。看看他怎么判断的。我们debug进去。
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	// 这里就是处理逻辑,我们再进去。
	Object handler = getHandlerInternal(request);
	// 省略没用的......
	return executionChain;
}

最后来到这里。org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	/**
		lookupPath:就是我们的这次请求的路径/user
		而我们一共有四个/user。分别是post get put delete所以这里directPathMatches 
		会拿回来四个结果。
	*/
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		// 这里会根据请求类型,返回其中对的上的,比如我们这次请求的是GET,那就返回GET的那个
		// /user,把这个请求的处理信息放在了matches集合里面
		addMatchingMappings(directPathMatches, matches, request);
	}
	// 如果一个也没有,全部封装进来,这里我们会找到一个。不走这里
	if (matches.isEmpty()) {
		// No choice but to go through all mappings...
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}

	if (!matches.isEmpty()) {
		// 取出找到的那一个,这样我们就找到了我们的controller和接口
		Match bestMatch = matches.get(0);
		// 如果找到了多个,哥们你url写重复了,抛出异常Ambiguous...之类的
		if (matches.size() > 1) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			bestMatch = matches.get(0);
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

这样我们就找到了我们的controller和接口,后面就是请求执行了,之前我们看过了,其实是反射。

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

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

相关文章

Nacos 部分漏洞整理

免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 1. Naco简介 微信公众号搜索:南风漏洞复现文库 该文…

element中 el-input 不更新的问题

需要在上面输入数字,下面一列都更改成一样的数字 batchChange(list,field,value){console.log(list,field,value);list.forEach((i,index) > {i[field]value;this.$set(list,index, JSON.parse(JSON.stringify(i)));});} 用 JSON.parse(JSON.stringify(i)) 转一下后,就可以…

langchain入门系列之八 langgraph多agent示例

在之前的文章中我们介绍了langgraph&#xff0c;并且用它做了一个小小的示例&#xff0c;在本文中&#xff0c;我们将使用智普清言来构建多agent 系统。百度千帆对langgraph支持较差(尤其是强制要求奇数偶数的role设置&#xff0c;传入messages的奇数偶数要求&#xff0c;让人有…

用好外呼机器人,帮助企业提升客户管理效率

外呼机器人&#xff0c;作为现代科技与企业管理的结合体&#xff0c;正在企业客户管理领域掀起革命性的变化。随着人工智能技术的不断进步&#xff0c;外呼机器人不仅实现了自动化呼叫&#xff0c;还能根据客户的语音情感进行相应的反馈和操作&#xff0c;极大地提高了客户满意…

AI大模型编写多线程并发框架(六十三):监听器优化·上

系列文章目录 文章目录 系列文章目录前言一、项目背景二、第七轮对话-补充异步执行代码三、第八轮对话-增加累计完成等字眼四、第九轮对话-线程安全优化五、第十轮对话-增加运行时数据七、参考文章 前言 在这个充满技术创新的时代&#xff0c;AI大模型正成为开发者们的新宠。它…

SpringBoot+Vue的图书管理系统【包含运行步骤】

SpringBootVue图书管理系统 一、项目介绍1. 图书信息管理2. 图书类型管理3. 图书借阅管理4. 用户管理 二、技术选型后端技术选型前端技术选型 三、运行步骤后端启动前端启动 四、项目演示源码获取方式 五、总结与展望 大家好&#xff0c;这里是程序猿代码之路&#xff01;随着信…

Linux基础1-基本指令7(其他常用指令,shell简介)

目录 1.uname 2.常用小指令 3.查看系统信息的其他指令 4.shell命令及其原理 4.1这里我们简单了解一下shell 4.2 shell存在的意义&#xff1f; 1.uname 如何查看计算机体系架构&#xff1f; uname -a,查看详细信息 uname -r 查看简要信息 2.常用小指令 TAB&#x…

Socket编程---UDP篇

目录 一. UDP协议 二. Socket编程 2.1 sockaddr家族 2.2 接口介绍 三. 服务端实现 四. 服务端调用实现 五. 客户端实现 六. 效果展示 一. UDP协议 何为UDP协议的含义&#xff0c;上篇粗略提及了一下TCP与UDP的区别&#xff1a; TCP&#xff1a; •…

将军百战死,程序十年成

将军百战死&#xff0c;程序十年成 十年前的 2014.8.3 我释出了动词算子式通用代码生成器的第一个完整版本 InfinityGPGenerator 0.6.5&#xff0c;即无垠式通用代码生成器 0.6.5。这是一个重大的里程碑。十年后&#xff0c;通用代码生成器已经是一个大家族。昨天&#xff0c;…

插入排序的动画展示与实现

排序学习思路&#xff1a;先实现单趟逻辑&#xff0c;在实现整体逻辑&#xff1b;先解决普遍情况&#xff0c;再解决特殊情况。 什么是插入排序 回忆下自己玩扑克牌的时候是怎么把手上的牌理顺的吧&#xff01;其实那就是插入排序&#xff0c;从左边往右边&#xff0c;把一张张…

强烈推荐!大模型辅助软件开发

图书推荐 作者介绍 很喜欢作者在书上的这句话了&#xff1a;是人类工程师的能力&#xff0c;而不是大模型的能力&#xff0c;决定了大模型协作式开发的上限。 本书内容 软件开发正在经历一场前所未有的范式变革。人工智能的飞速发展&#xff0c;特别是大型语言模型所取得的成…

【Python篇】Python 类和对象:详细讲解(上篇)

文章目录 Python 类和对象&#xff1a;详细讲解1. 什么是类&#xff08;Class&#xff09;类的定义 2. 什么是对象&#xff08;Object&#xff09;创建对象 3. 属性和方法属性&#xff08;Attributes&#xff09;方法&#xff08;Methods&#xff09;在类中定义属性和方法使用对…

使用facebook开源prophet模型预测上证指数etf股价

可以图个乐&#xff0c;没有那么准确&#xff0c;可能还需要更深入的研究分析 蓝线是预测的2024年的走势&#xff0c;绿线是实际走势&#xff0c;红线是历史和未来的分界线。结果上有蛮多差异的。 # 测试预测2024年 coded by luke 伊玛目的门徒 import akshare as ak impor…

基于Java的心灵治愈交流平台

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;Java语言&#xff1b;Spring Boot框架 工具&#xff1a;IDEA/Eclipse、Navicat 系统展示 首页 心…

美畅物联丨实时通信新篇章:Spring Boot与WebSocket的深度融合实践

在当今 Web 应用开发领域&#xff0c;实时通信功能已跃升为不可或缺的基石&#xff0c;特别是在即时消息传递、沉浸式在线游戏体验以及精准实时数据监控等关键领域。WebSocket协议&#xff0c;凭借其独特的全双工通讯机制&#xff0c;在单个持久连接上实现了服务器与客户端之间…

软件测试面试题「值得收藏」

1、B/S架构和C/S架构区别&#xff1f; 1、架构不同&#xff1a;B/S架构是浏览器/服务器架构&#xff0c;C/S架构是客户端/服务器架构。 2、客户端不同&#xff1a;B/S架构的客户端是浏览器&#xff0c;C/S架构的客户端是客户端程序。 3、功能不同&#xff1a;B/S架构主要用于…

PhpStorm2024版设置自动换行(软换行)

Settings > Editor > General > Soft Wraps 选中并加上对应的文件

链动2+1小程序定制开发在餐饮品牌重塑中的应用探索——以“妈妈饺子馆”为例

摘要&#xff1a;随着互联网技术的飞速发展&#xff0c;餐饮业正经历着前所未有的变革。传统餐饮企业如何在激烈的市场竞争中脱颖而出&#xff0c;成为行业关注的焦点。本文以“妈妈饺子馆”为例&#xff0c;探讨链动21小程序定制开发在餐饮品牌重塑中的应用&#xff0c;特别是…

8 大模型微调

大部分接触大模型的同学大家可能都受限于资源的限制&#xff0c;无法对大模型重新训练。那么如何解决这一困境&#xff1f;我们暂且假定大模型为通用化模型&#xff0c;但是在某一方面的专业领域知识却不强&#xff0c;如果使用专业领域知识重新训练调整&#xff0c;这对资源还…

如何选到好的宠物空气净化器?有没有推荐的品牌?

从最初的手忙脚乱、对宠物养护知识一无所知的新手小白&#xff0c;到如今能够游刃有余地处理各种宠物问题、养宠多年的资深铲屎官&#xff0c;这么久&#xff0c;我也积累了不少的经验&#xff0c;就是希望能为自家的毛孩子提供最健康、最适合的生活。 这么久的养猫历程遇见的…