SpringMVC 源码剖析

news2024/11/16 23:36:29

SpringMVC 源码剖析

在这里插入图片描述

0 从源码角度分析SpringMVC执行流程

// 前端控制器,SpringMVC最核心的类
public class DispatcherServlet extends FrameworkServlet {
	// 前端控制器最核心的方法,这个方法是负责处理请求的,一次请求,调用一次 doDispatch 方法。
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 通过请求获取处理器
		// 请求:http://localhost:8080/springmvc/hello (有URI)
		// 根据请求路径来获取对应的要执行的处理器
		// 实际上返回的是一个处理器执行链对象
		// 这个执行链(链条)把谁串起来了呢?把这一次请求要执行的所有拦截器和处理器串起来了。
		// HandlerExecutionChain是一次请求对应一个对象
		HandlerExecutionChain mappedHandler = getHandler(request);
		
		// 根据处理器获取处理器适配器对象
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Handler就是我们写的Controller

		// 执行该请求对应的所有拦截器中的 preHandle 方法
		if (!mappedHandler.applyPreHandle(processedRequest, response)) {
			return;
		}

		// 调用处理器方法,返回ModelAndView对象
		// 在这里进行的数据绑定,实际上调用处理器方法之前要给处理器方法传参
		// 需要传参的话,这个参数实际上是要经过一个复杂的数据绑定过程(将前端提交的表单数据转换成POJO对象)
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

		// 执行该请求对应的所有拦截器中的 postHandle 方法
		mappedHandler.applyPostHandle(processedRequest, response, mv);

		// 处理分发结果(本质上就是响应结果到浏览器)
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
	}

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
		// 渲染
		render(mv, request, response);
		// 执行该请求所对应的所有拦截器的afterCompletion方法
		mappedHandler.triggerAfterCompletion(request, response, null);
	}

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 通过视图解析器进行解析,返回视图View对象
		View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		// 调用视图对象的渲染方法(完成响应)
		view.render(mv.getModelInternal(), request, response);
	}

	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {
		// 视图解析器
		ViewResolver viewResolver;
		// 通过视图解析器解析返回视图对象View
		View view = viewResolver.resolveViewName(viewName, locale);
	}
}


// 视图解析器接口
public interface ViewResolver {
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

// 视图解析器接口实现类也很多:ThymeleafViewResolver、InternalResourceViewResolver

// 视图接口
public interface View{
	void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
			throws Exception;
}

// 每一个接口肯定是有接口下的实现类,例如View接口的实现类:ThymeleafView、InternalResourceView....

1 关于根据请求获取处理器执行链

分析这一行代码:
HandlerExecutionChain mappedHandler = getHandler(request);

1. HandlerExecutionChain:处理器执行链对象

2. HandlerExecutionChain中的属性:
	public class HandlerExecutionChain{
		// 底层对应的是一个HandlerMethod对象
		// 处理器方法对象
		Object handler = new HandlerMethod(.....);
		// 该请求对应的所有的拦截器按照顺序放到了ArrayList集合中
		// 所有的拦截器对象也都是在服务器启动的时候都创建好。
		List<HandlerInterceptor> interceptorList;
	}

3. HandlerMethod 是什么?
	
	HandlerMethod是最核心的要执行的目标,翻译为:处理器方法。
	注意:HandlerMethod 是在web服务器启动时初始化spring容器的时候,就创建好了。
	这个类当中比较重要的属性包括:beanName和Method
	例如,以下代码:
		@Controller("userController")
		public class UserController{
			@RequestMapping("/login")
			public String login(User user){
				return ....
			}
		}
	那么以上代码对应了一个HandlerMethod对象:
		public class HandlerMethod{
			private String beanName = "userController";
			private Method loginMethod;
		}

4. getHandler(request);
	这个方法还是在DispatcherServlet类中。
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				// 通过合适的 HandlerMapping才能获取到 HandlerExecutionChain对象。
				// 如果你处理器方法使用了 @RequestMapping注解,那么以下代码中的mapping是:RequestMappingHandlerMapping对象。
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

	重点:
		我们处理请求的第一步代码是:HandlerExecutionChain mappedHandler = getHandler(request);
		其本质上是调用了:HandlerExecutionChain handler = mapping.getHandler(request);

	mapping变量就是 HandlerMappingHandlerMapping是一个接口:
		翻译为处理器映射器,专门负责映射的。就是本质上根据请求路径去映射处理器方法的。
		HandlerMapping接口下有很多实现类:
			例如其中一个比较有名的,常用的:RequestMappingHandlerMapping
			这个 RequestMappingHandlerMapping 叫做:@RequestMapping注解专用的处理器映射器对象。

			当然,如果你没有使用 @RequestMapping注解,也可以写xml配置文件来进行映射,那个时候对应的就是其他的HandlerMapping接口的实现类了。
	
	HandlerMapping 对象也是在服务器启动阶段创建的,所有的HandlerMapping对象都是在服务器启动阶段创建,并且存放到集合中。
	public class DispatcherServlet{
		List<HandlerMapping> handlerMappings;
	}

5. RequestMappingHandlerMapping中的 getHandler(request);
	HandlerExecutionChain handler = mapping.getHandler(request);
	
	mapping.getHandler(request);这个方法底层一定是获取了 HandlerMethod 对象,将其赋值给 HandlerExecutionChain的handler属性

	public class RequestMappingHandlerMapping extends AbstractHandlerMethodMapping{
		protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
			super.registerHandlerMethod(handler, method, mapping);
			updateConsumesCondition(mapping, method);
		}
	}

	public class AbstractHandlerMethodMapping{
		protected void registerHandlerMethod(Object handler, Method method, T mapping) {
			this.mappingRegistry.register(mapping, handler, method);
		}

		public void register(T mapping, Object handler, Method method) {
			HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		}

		protected HandlerMethod createHandlerMethod(Object handler, Method method) {
			if (handler instanceof String beanName) {
				return new HandlerMethod(beanName,
						obtainApplicationContext().getAutowireCapableBeanFactory(),
						obtainApplicationContext(),
						method);
			}
			return new HandlerMethod(handler, method);
		}
	}
	

这一步牵连到的类有哪些:
	HandlerExecutionChain
	HandlerMethod
	HandlerInterceptor
	HandlerMapping
		RequestMappingHandlerMapping(是HandlerMaping接口的实现)

2 关于根据处理器来获取处理器适配器

分析:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

1. 底层使用了适配器模式。

2. 每一个处理器(我们自己写的Controller),都有自己适合的处理器适配器。

3.SpringMVC当中处理器适配器也有很多种,其中一个比较有名的,常用的处理器适配器是:RequestMappingHandlerAdapter
这个处理器适配器是专门处理 “处理器方法”上有 @RequestMapping 注解的。

4. mappedHandler.getHandler() 获取的是 HandlerMethod 对象

5. HandlerAdapter也是一个接口:
	其中有一个常用的实现类:RequestMappingHandlerAdapter

6. 在服务器启动阶段,所有的 HandlerAdapter接口的实现类都会创建出来。在服务器启动阶段!!!!!!
	List<HandlerAdapter> handlerAdapters;

7. HandlerAdapter接口非常重要,通过这个接口来调用最终的 HandlerMethod8. HandlerAdapter是适配器,是对 HandlerMethod 进行的适配。

9.DispatcherServlet类中,如下代码:
	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
		if (this.handlerAdapters != null) {
			for (HandlerAdapter adapter : this.handlerAdapters) {
				if (adapter.supports(handler)) {
					return adapter;
				}
			}
		}
	}

3 关于执行请求对应的拦截器preHandle

关于执行请求对应的拦截器的preHandle方法

DispatcherServlet:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
	return;
}


HandlerExecutionChainboolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	for (int i = 0; i < this.interceptorList.size(); i++) {
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		if (!interceptor.preHandle(request, response, this.handler)) {
			triggerAfterCompletion(request, response, null);
			return false;
		}
		this.interceptorIndex = i;
	}
	return true;
}

遍历List集合,从List集合中取出每一个 HandlerInterceptor对象,调用 preHandle,i++,可见是顺序调用。

4 关于调用处理器方法

关于调用处理器方法:
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

	ha 是处理器适配器

	mv 是ModelAndView对象

	这个方法是最核心的,调用请求路径对应的HandlerMethod。(调用处理器方法。)


ha是HandlerAdapter,如果是 @RequestMapping 注解对应的,那么就是 RequestMappingHandlerAdapterRequestMappingHandlerAdapterprotected ModelAndView handleInternal(HttpServletRequest request,
				HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
		// 获取一个数据绑定工厂
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		// 获取一个可调用的处理器方法
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		// 给可调用的方法绑定数据
		invocableMethod.setDataBinderFactory(binderFactory);
		// 给可调用的方法设置参数
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
		// 可调用的方法执行了。
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
	}HandlerAdapter中做的核心事情:
	将前端提交的form数据通过 HttpMessageConverter 将其转换成 POJO对象。(数据转换)
	并将数据绑定到 HandlerMethod 对象上。
	调用HandlerMethod。
	返回 ModelAndView

5 关于执行请求对应的拦截器的postHandle

DispatcherServlet:

	mappedHandler.applyPostHandle(processedRequest, response, mv);

HandlerExecutionChain:
	
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
			throws Exception {

		for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}

通过源码解决,可以很轻松的看到,从List集合中逆序(i--)逐一取出拦截器对象,并且调用拦截器的 postHandle方法。

6 关于处理分发结果

public class DispatcherServlet{

	// 处理分发结果
	processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
		// 渲染
		render(mv, request, response);
	}

	protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 通过视图解析器进行解析,返回视图View对象
		View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
		// 调用视图对象的渲染方法(完成响应)
		view.render(mv.getModelInternal(), request, response);
	}

	protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
			Locale locale, HttpServletRequest request) throws Exception {
		// 视图解析器
		ViewResolver viewResolver;
		// 通过视图解析器解析返回视图对象View
		View view = viewResolver.resolveViewName(viewName, locale);
	}
}


// 视图解析器接口
public interface ViewResolver {
	View resolveViewName(String viewName, Locale locale) throws Exception;
}

// 视图解析器接口实现类也很多:ThymeleafViewResolver、InternalResourceViewResolver

// 视图接口
public interface View{
	void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
			throws Exception;
}

// 每一个接口肯定是有接口下的实现类,例如View接口的实现类:ThymeleafView、InternalResourceView....

7 关于执行拦截器的afterCompletion方法

DispatcherServlet:

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
		// 渲染
		render(mv, request, response);
		// 执行该请求所对应的所有拦截器的afterCompletion方法
		mappedHandler.triggerAfterCompletion(request, response, null);
	}

HandlerExecutionChain:
	
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = this.interceptorList.get(i);
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}

	通过源码可以看出,也是通过逆序(i--)的方式进行拦截器的调用,调用拦截器的afterCompletion方法。

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

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

相关文章

Redis入门到通关之数据结构解析-IntSet

文章目录 概述IntSet升级简易源码总结 欢迎来到 请回答1024 的博客 &#x1f34e;&#x1f34e;&#x1f34e;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开发者。 …

详细谈电脑ip、域名、内网、外网、localhost、127.0.0.1、网关等通讯基础知识(易懂)

1. ip地址与域名的定义以及其关系 ip地址的定义&#xff1a; IP地址&#xff08;Internet Protocol Address&#xff09;是指互联网协议地址&#xff0c;又译为网际协议地址。 IP地址是IP协议提供的一种统一的地址格式&#xff0c;它为互联网上的每一个网络和每一台主机分配一…

【ARM 裸机】BSP 工程管理

回顾一下上一节&#xff1a;【ARM 裸机】NXP 官方 SDK 使用&#xff0c;我们发现工程文件夹里面各种文件非常凌乱&#xff1b; 那么为了模块化整理代码&#xff0c;使得同一个属性的文件存放在同一个目录里面&#xff0c;所以学习 BSP 工程管理非常有必要。 1、准备工作 新建…

短视频账号矩阵系统===4年技术源头打磨

短视频矩阵系统技术源头打磨需要从多个方面入手&#xff0c;以下是一些建议&#xff1a; 1. 基础技术研发&#xff1a;不断投入资金和人力进行基础技术研发&#xff0c;包括但不限于视频处理、人工智能、大数据等技术&#xff0c;以提高短视频矩阵系统的性能和稳定性。 2. 优化…

多进程编程:原理、技术与应用

title: 多进程编程&#xff1a;原理、技术与应用 date: 2024/4/26 12:14:47 updated: 2024/4/26 12:14:47 categories: 后端开发 tags: 多进程并发编程网络服务分布式系统任务处理进程池线程对比 第一章&#xff1a;进程与线程 进程与线程的概念及区别&#xff1a; 进程&am…

四信智能化感知与控制方案,助推灌区续建配套与现代化改造建设

“十四五”明确提到推进大中型灌区节水改造和精细化管理&#xff0c;建设节水灌溉骨干工程&#xff0c;同步推进水价综合改革。 灌区是保障国家粮食安全的重要基础性设施&#xff0c;是实施乡村振兴战略的水利支撑。灌区续建配套与现代化改造是实施乡村振兴战略一项重要任务。为…

el-tab面板添加折叠按钮方法

折叠后 <template><div class"page-type-left-wrap"><div class"page-type-left-wrap-info nav-link" :class"{ leftCollapse }"><el-tabs v-model"activeName" class"page-tabs" tab-change"han…

Git系列:Refs与Reflog

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【经典算法】LeetCode104二叉树的最大深度(Java/C/Python3实现含注释说明,Easy)

目录 题目描述思路及实现方式一&#xff1a;递归思路代码实现Java版本C语言版本Python3版本Go语言版本 复杂度分析 方式二&#xff1a;广度优先搜索(BFS)思路代码实现Java版本C语言版本Python3版本 复杂度分析 总结相似题目 标签(题目类型)&#xff1a;树、深度优先搜索(DFS)、…

B站无限评论暴力截留协议及教程

B站无限评论暴力截留协议及教程 B站无限评论暴力截留协议及教程&#xff0c;需要抓CK &#xff0c;教程里面有讲如何抓取 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

PHP 错误 Unparenthesized `a ? b : c ? d : e` is not supported

最近在一个新的服务器上测试一些老代码的时候得到了类似上面的错误&#xff1a; [Thu Apr 25 07:37:34.139768 2024] [php:error] [pid 691410] [client 192.168.1.229:57183] PHP Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : …

DeblurGAN-v2: Deblurring (Orders-of-Magnitude) Faster and Better

文章目录 摘要1、引言2、相关工作2.1、图像去模糊2.2、生成对抗网络 3、DeblurGAN-v2 架构3.1、特征金字塔去模糊3.2、骨干网络的选择&#xff1a;性能与效率之间的权衡3.3、双尺度RaGAN-LS判别器3.4、训练数据集 4、实验评估4.1、实现细节4.2、在GoPro数据集上的定量评估4.3、…

服务器数据恢复—存储硬盘坏道,指示灯亮黄色的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 一台某品牌EqualLogic PS系列某型号存储&#xff0c;存储中有一组由16块SAS硬盘组建的RAID5磁盘阵列&#xff0c;RAID5上划分VMFS文件系统存放虚拟机文件。存储系统上层一共分了4个卷。 raid5阵列中磁盘出现故障&#xff0c;有2块硬盘…

5款文案生成器,帮你智能写作优秀文案

在当今数字化时代&#xff0c;文案写作是营销和传播领域中至关重要的一环。然而&#xff0c;对于许多人来说&#xff0c;撰写引人注目且有吸引力的文案可能是一项具有挑战性的任务。这就是为什么文案生成器变得如此受欢迎的原因。通过结合人工智能和自然语言处理技术&#xff0…

C++之STL-String

目录 一、STL简介 1.1 什么是STL 1.2 STL的版本 1.3 STL的六大组件 ​编辑 1.4 STL的重要性 二、String类 2.1 Sting类的简介 2.2 string之构造函数 2.3 string类对象的容量操作 2.3.1 size() 2.3.2 length() 2.3.3 capacity() 2.3.4 empty() 2.3.5 clear() 2.3.6…

JDBC查询大数据时怎么防止内存溢出-流式查询

文章目录 1.前言2.流式查询介绍3.使用流式查询3.1不开启流式查询的内存占用情况3.2开启流式查询的内存占用情况 4.开启流式查询的注意点 1.前言 在使用 JDBC 查询大数据时&#xff0c;由于 JDBC 默认将整个结果集加载到内存中&#xff0c;当查询结果集过大时&#xff0c;很容易…

【项目】YOLOv8/YOLOv5/YOLOv9半监督ssod火灾烟雾检测(YOLOv8_ssod)

假期闲来无事找到一份火灾烟雾数据集&#xff0c;自己又补充标注了一些&#xff0c;通过论文检索发现现在的火灾检测工作主要局限于对新场景的泛化性不够强&#xff0c;所以想着用半监督&#xff0c;扩充数据集的方法解决这个问题&#xff0c;所以本文结合使用现在检测精度较高…

Canal入门使用

说明&#xff1a;canal [kə’nl]&#xff0c;译意为水道/管道/沟渠&#xff0c;主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff08;官方介绍&#xff09;。一言以蔽之&#xff0c;Canal是一款实现数据同步的组件。可以实现数据库之间、数…

网络相关知识总结

1、网口设置 网口设置IP&#xff0c;即操作/etc/sysconfig/network-scripts路径下的ifcfg-xx文件 主要参数详解&#xff1a; DEVICE:网口名 ONBOOT&#xff1a;表示启动系统时是否激活网卡&#xff0c;yes为激活&#xff0c;no不激活 HWADDR:mac值 DEFROUTE://默认路由设置…

[C++基础学习]----01-C++数据类型详解

前言 C是一种静态类型的编程语言&#xff0c;它提供了丰富的数据类型来存储和操作数据。这些数据类型为C程序员提供了丰富的选择&#xff0c;可以根据具体需求来选择最合适的类型来存储和操作数据。下面详细解释一些常见的C数据类型&#xff0c;包括其原理和使用方法&#xff1…