Spring 源码解析 -- SpringWeb请求映射解析

SerDes学习笔记:接收机CTLE均衡架构示例(USB3.1、PCIe3.0\4.0\5.0) 本文分析了USB3.1和PCIe标准中连续时间线性均衡器(CTLE)的设计演进。USB3.1Gen1采用一阶CTLE,其传递函数在长信道下具有有源滤波器特性;Gen2版本延续类似架构,通过Adc参数调节零点频率。PCIeGen3/4保持一阶结构但调整极点位置,Gen5升级为二阶传递函数,扩展Adc调节范围至-5dB~-15dB。研究表明,CTLE能有效改善信号完整性,通过参数优化可平衡信号衰减与码间干扰抑制。 阅读详情

Spring 源码解析 – SpringWeb请求映射解析


简介

基于上篇请求路径初步探索,了解到了一个请求到具体处理方法的大致路径,本篇就继续探索,看下路径是如何匹配到处理方法的

概览

基于上篇:Spring Web 请求初探

我们大致定位到了请求路径到处理方法的关键代码在类中:DispatcherServlet.java

具体的代码如下:

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

mappedHandler 的获取就是关键了,下面我们就具体看下如何获取请求的mappedHandler的

源码解析

获取 mappedHandler

在类:DispatcherServlet.java 中,我们定位到 mappedHandler 获取的关键代码

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
}

通过上面的代码,我们可以看到是通过请求进行获取,我们还注意到了为null的情况,如下代码,大意就是经典的404

/**
* No handler found -> set appropriate HTTP response status.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception if preparing the response failed
*/
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
	if (pageNotFoundLogger.isWarnEnabled()) {
		pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
	}
	if (this.throwExceptionIfNoHandlerFound) {
		throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
				new ServletServerHttpRequest(request).getHeaders());
	}
	else {
		response.sendError(HttpServletResponse.SC_NOT_FOUND);
	}
}

我们进入 getHandler 函数,看看其具体处理逻辑,代码如下:

/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@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;
}

从上面代码大意可以看出:循环遍历一个关于请求匹配的Map,如果某个Handler匹配成功则返回

这个Map是如何初始化、具体包含哪些内容?

HandlerExecutionChain 具体是什么?

这部分疑问后面再看,感觉是个大工程,当前并不影响本篇的解析

请求与Mapper匹配

接着跟下去,到类:AbstractHandlerMethodMapping.java

我们来到了一个比较关键的处理,我们可以看到这里有相关的匹配逻辑,里面有很多的处理,有些目前都不知道有啥作用,后面查查资料后咱们再回过头来看看

	@Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);

		// 什么情况下会匹配到默认的Handler?
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}

		// 什么情况下获取到的Bean是一个String?
		// 需要从 obtainApplicationContext 中获取
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}

		// 什么情况下会用到缓存?
		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

其中的 getHandlerExecutionChain 还挺可疑,感觉也是关键代码,给设置了一堆的我们熟悉的 Interceptors,这里做个标记,后面再探索下

	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
				(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

		for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
			if (interceptor instanceof MappedInterceptor) {
				MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
				if (mappedInterceptor.matches(request)) {
					chain.addInterceptor(mappedInterceptor.getInterceptor());
				}
			}
			else {
				chain.addInterceptor(interceptor);
			}
		}
		return chain;
	}

Handler匹配

下面就跟着断点来到了:AbstractHandlerMapping.java

看到下面的关键代码:在查找匹配的时候上了一个锁,进入具体的查找逻辑

@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	// 这里一些处理得到我们的请求路径:”/"
	String lookupPath = initLookupPath(request);
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

我们进入类:AbstractHandlerMethodMapping.java 中,查看具体的处理逻辑

我们可以看到这里进行了一系列的匹配,如果为空还能取默认值,这个默认值是个啥?

并且还有多个匹配上的还不会报错的情况,瞬间感觉Web这块还是有很多门道啊,但这些细节后面我们再看看,继续看下匹配的逻辑

	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		// 在这里获取到了相关的Handler,就是一个简单的Map取值(如后面的代码),那个Map如何初始化的得后面看看了
		List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}

		// 如果没有匹配上,这个默认值又是如何处理的呢?
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}

		// 这里看到一些非常有趣的处理
		// 如果同时匹配上了两个,好像不是一定报错?
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			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)) {
					for (Match match : matches) {
						if (match.hasCorsConfig()) {
							return PREFLIGHT_AMBIGUOUS_MATCH;
						}
					}
				}
				else {
					Match secondBestMatch = matches.get(1);
					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
						Method m1 = bestMatch.getHandlerMethod().getMethod();
						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
						String uri = request.getRequestURI();
						throw new IllegalStateException(
								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
					}
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}

一个简单的Map取值(如后面的代码),那个Map如何初始化的得后面看看了

class MappingRegistry {

	private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

	@Nullable
	public List<T> getMappingsByDirectPath(String urlPath) {
		return this.pathLookup.get(urlPath);
	}
}

到这里就匹配成功并返回了,但通过调试看到不是我们熟悉的类和方法

继续看看这行的具体处理逻辑:return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);

看看类 HandlerMethod 的一些方法和构造函数:

下面函数我们看到了属性的获取Bean

	public HandlerMethod createWithResolvedBean() {
		Object handler = this.bean;
		if (this.bean instanceof String) {
			Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
			String beanName = (String) this.bean;
			handler = this.beanFactory.getBean(beanName);
		}
		return new HandlerMethod(this, handler);
	}

在其构造函数中,直接将Bean和Method进行了初始化,这个如何初始化的我们后面再看

	public HandlerMethod(Object bean, Method method) {
		Assert.notNull(bean, "Bean is required");
		Assert.notNull(method, "Method is required");
		this.bean = bean;
		this.beanFactory = null;
		this.beanType = ClassUtils.getUserClass(bean);
		this.method = method;
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		this.parameters = initMethodParameters();
		evaluateResponseStatus();
		this.description = initDescription(this.beanType, this.method);
	}

总结

本篇文章探索了下具体的请求如何匹配到处理方法的相关处理函数,了解到了核心逻辑就是遍历一个: handlerMappings

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

后面的具体匹配逻辑也有很多值得探索的细节,但接下来更重要的是弄清楚这个: handlerMappings ,是如何初始化和加载的

下面的文章我们会继续探索

Vue3 - Element Plus上传组件el-upload上传多个文件时,让on-change监听事件仅执行一次并获取最终文件列表方法(开启多选文件并手动上传,拿到文件列表统一循环调用接口上传) vue3,elementplus,Upload上传,el-upload,文件上传组件,上传组件on-change里执行多次,取消自动上传后change重复执行问题,upload上传多个文件后导致on-change执行多次拿不到最终文件列表,Element-plus的upload组件on-change不断执行怎么办,上传多个文件多次执行调用,上传多个文件时如何只执行一次on-change方法拿到最终的文件列表,vue3elementplus上传组件开启多个上传后导致change方法执行好几次,仅调用一 阅读详情

相关推荐

【学习笔记】计算机网络 第八章 无线局域网(WLAN)

计算机网络 第八章 无线局域网(WLAN)

Vera_shsf的博客 1240

SpringBoot---web-->>请求映射原理【快乐的源码】

Web---请求映射原理 首先,在原生servlet中处理请求的是doget和dopost方法,在spring中处理请求的首先是dispatcherservlet,所以先来到DispatcherServlet源码 发现它继承了FrameworkServlet,这个FrameworkServlet继承了HttpServletBean,说明dispatcherServlet是一个原生servlet,FrameworkServlet继承了HttpServletBean,HttpServletBean继承了Ht

wowo 602

nCode:GlyphWorks案例教程一

nCode GlyphWorks案例一Comparing Frequency Content of Two Different Tests案例文件目标配置选择数据 Comparing Frequency Content of Two Different Tests 在本案例中,我们将比较来自两个不同测试的六个加速计通道的频率内容。将三轴加速度计放置在车辆的左前角和右前角,然后分别在两个不同的试验路面上行驶12分钟以上。 案例文件 此案例的文件(采用nCode DAC格式)位于nCode安装目录下的\demo

追月行的博客 1万+

spring源码分析之spring web模块分析

Spring的Web模块

qq_39350797的博客 387

手把手教你用smarteye搭建免费GB28181监控平台(含海康设备接入避坑指南)

本文提供了一份基于SmartEye搭建免费GB28181视频监控平台的实战指南。详细介绍了从服务器部署、GB28181协议核心配置,到海康威视等主流设备接入的完整流程,并重点分享了设备注册失败、无法预览等常见问题的深度排错方法,旨在帮助中小企业及开发者低成本实现多品牌监控设备的统一管理。

pz89012345的博客 326

深入解析uncompyle6反编译pyc文件及常见错误修复指南

本文深入解析了使用uncompyle6工具反编译Python字节码文件(.pyc)的完整流程。针对实践中最高频的KeyError和Python版本范围限制两大错误,提供了详细的修复方案与步骤指南,并分享了处理复杂反编译结果的高级技巧与安全实践,是进行代码恢复、调试与分析的重要参考。

uber9的博客 534

Spring源码深度解析笔记(10)——默认标签的解析

之前提到过Spring中的标签包括默认标签和自定义标签,而这两种标签的用法以及解析方式存在着很大不同。 默认标签的解析是在parseDefaultElement函数进行的,函数中的功能逻辑一目了然,分别对4中不同标签(import、alisa、bean和beans)做了不同处理。 private void parseDefaultElement(Element ele,BeanDefinitio...

wuyingya_12的博客 2139

2024高校网络安全管理运维赛题目--复现+题目+wp

官方writeup集思广益比较好的备份。

书山有路勤为径,学海无涯苦作舟 1319

springboot的web源码分析

浏览器器访问就能展示图片的信息,如果显示不出来,把target删除掉,重新启动项目。我们把静态资源放到static目录下。

qq_21277357的博客 571

HTB_Bike靶机之模板注入

船到桥头自然直 文章目录信息收集模板引擎SSIT 模板注入后放答案 信息收集 日常 A 扫描,两个端口 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA) | 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:.

网络安全学习 797

美的集团-数据挖掘工程师面试分享

美的集团-数据挖掘工程师面试分享

510

Spring源码深度解析

本文对spring IOC/AOP 源码做详了细解读

qq_31359923的博客 5692

华为OD机考真题 新系统 - 小菊的寻宝之旅(Java/Py/C/C++/Js/Go)

华为OD机试真题 新系统【小菊的寻宝之旅】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【小菊的寻宝之旅】多语言题解。

qq_45776114的博客 618

Spring源码解析 -- SpringWeb请求映射Map初始化

在上篇文章中,大致解析了Spring如何将请求路径与处理方法进行映射,但映射相关的初始化对于我们来说还是一团迷雾 本篇文章就来探索下,请求路径和处理方法的映射,是如何进行初始化的

github_35735591的博客 329

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Spring源码深度解析:一、Spring整体架构和源码环境搭建

Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层.Spring AOP:集成了所有AOP功能。

知行 3139

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

探究SpringWeb对于请求的处理过程解析

首先在分析spring对请求处理之前之前,首先需要了解DispatcherServlet,它是Spring MVC的核心,负责接收HTTP请求,并根据请求信息分发到相应的Controller进行处理。DispatcherServlet重要特性如下:前端控制器模式:在Spring MVC框架中,DispatcherServlet实现了前端控制器设计模式。这个模式的主要思想是提供一个中心点,所有的请求将先到达这个中心点,然后由它进行分发。

y473158Yansu的博客 124

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

SpringWeb MVC加载源码浅析

1.Spring mvc,依靠DispatcherServlet请求分发器来处理指定URL的请求,他本身就直接或间接继承或者实现了 ApplicationContextAware、HttpServletBean、HttpServlet,也就是说明这个类同时具备了请求分发、ioc处理以及视图解析的功能。下面请看关系图: 下面请看部分属性 //请求url 解析,通过请求获取所属Handler也...

shaifu3328的博客 482

Spring5源码解析-论Spring DispatcherServlet的生命周期

Spring Web框架架构的主要部分是DispatcherServlet。也就是本文中重点介绍的对象。 在本文的第一部分中,我们将看到基于Spring的DispatcherServlet的主要概念:前端控制器模式。第二部分将专门介绍Spring应用程序中的执行链。接下来是DispatcherServlet类的解释。在最后一部分,我们将尝试开发一个自定义的dispatcher servlet。 ...

IT哈哈的博客 309

探究SpringWeb对于请求的处理过程

首先在分析spring对请求处理之前之前,首先需要了解DispatcherServlet,它是Spring MVC的核心,负责接收HTTP请求,并根据请求信息分发到相应的Controller进行处理。DispatcherServlet重要特性如下:前端控制器模式:在Spring MVC框架中,DispatcherServlet实现了前端控制器设计模式。这个模式的主要思想是提供一个中心点,所有的请求将先到达这个中心点,然后由它进行分发。

Dokii_i的博客 1200

深入剖析Spring Web源码(十九) - 整理的文档和日志的索引(第一版)

整理的文档 把所有的《深入剖析Spring Web》系列日志整理成为文档,供大家下载阅读,希望对大家有所帮助。里面有些章节尚未完成,所以称为第一版。希望不久的将来,能把没有完成的章节在第二版里尽快完成。如果您有任何建议或者发现任何错误,请留言。我会根据大家的意见更改文章内容,格式,组织方式等等。 请登陆优快云下载文档,下载此文档不需要资源分,下载地址如下, Word格式下载连接PDF格式下载连接HTML格式下载连接 我的下载主页  日志的索引 整理了所有的《深入剖析Spring Web》系列日志的地址,并

云时代架构 1万+

springmvc01-spring请求流程源码解析

springmvc启动初始化流程和请求解析流程

d495435207的博客 192

探究SpringWeb对于请求的处理过程322

首先在分析spring对请求处理之前之前,首先需要了解DispatcherServlet,它是Spring MVC的核心,负责接收HTTP请求,并根据请求信息分发到相应的Controller进行处理。DispatcherServlet重要特性如下:前端控制器模式:在Spring MVC框架中,DispatcherServlet实现了前端控制器设计模式。这个模式的主要思想是提供一个中心点,所有的请求将先到达这个中心点,然后由它进行分发。

y473158Yansu的博客 129

spring源码---创建URL与controller方法映射

1、 AbstractHandlerMethodMapping类实现了InitializingBean,在InitializingBean中有一个afterPropertiesSet方法。看这个方法的说明: 大概意思是:此方法允许bean实例在设置了所有bean属性后执行其总体配置和最终初始化的验证。会调用AbstractHandlerMethodMapping的afterPropertiesSet方法。 2、afterPropertiesSet调用initHandlerMethods,遍.

sklaaaa的博客 758

【Spring Boot】web开发相关源码分析

结合源码,介绍spring boot是如何实现web相关功能的,包括Rest使用、请求映射等

Decade0712的博客 476
上一篇: Spring源码解析系列:Spring Web 请求初探
下一篇: Spring源码解析 -- SpringWeb请求映射Map初始化
_萧_
博客等级 码龄10年 55粉丝 157原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值