SpringMVC 源码剖析

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方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据类型总结

1 引言 在计算机的世界里&#xff0c;数据类型是被人类定义出来的&#xff0c;方便人去更好地理解、辨别数据。计算机只能识别二进制数&#xff0c;不可能要求写代码时&#xff0c;只是输入一些0/1的东西。通过定义数据类型&#xff0c;可以让人和计算机更好地“沟通”&#x…

图像处理|关于二维傅里叶变换的学习笔记(实用版)

因为图像至少是2D的&#xff0c;所以在数字图像处理中使用的都是2D-傅里叶变换。 1.什么是傅里叶变换(DFT)&#xff1f;傅里叶变换是将图像从空间域转换到频域&#xff0c;其逆变换是将图像从频域转到空间域。 物理意义是&#xff1a; 2.频谱图怎么看&#xff1f;傅里叶频谱图…

亚马逊、ozon、美客多等平台的测评技术核心:提升跨境电商业绩的关键要素

现今&#xff0c;越来越多的跨境卖家开始深入了解测评自养号这一领域&#xff0c;他们希望通过优化运营来降低成本并增加利润。在整个测评工作中&#xff0c;测评技术是非常关键的一环。只有不断学习、保持冷静&#xff0c;我们才能不断提升测评能力&#xff0c;从而获得更多机…

Bentley二次开发教程22-文件及模型管理-材质、图层

材质 材质主要用于对元素进行材质贴图&#xff0c;以表现实际的材料样式。材质表中包含材质表&#xff0c;材质面板以及材质。而其属性记录了反射等多种属性以表达实际材质效果。 创建材质 当我们需要创建自定义的材质时&#xff0c;对应的&#xff0c;需要依次创建材质表&…

JavaScript:js实现在线五子棋人机(人人)对弈

在线五子棋人机对弈 全部使用前端技术,使用HTML,CSS以及JS进行实现. 棋盘在后端就是一个15*15的二维数组 页面设计 页面设计的比较粗糙 主要使用js自带的canvas画布进行绘画 HTML代码如下: <div class"outer"><canvas id"canvas" height&qu…

男生一般穿什么裤子好看?五大爆款男装精选测评!

男生裤子要怎么选才能找到适合自己的裤子呢&#xff1f;这肯定是大家选裤子时经常出现的一个疑问了&#xff0c;现在的市面上虽然款式风格非常多&#xff0c;但是由于品牌鱼龙混杂的原因&#xff0c;不同的裤子质量也参差不齐。为了帮助各位男同胞能选到适合自己的裤子&#xf…

centos7使用源码安装方式redis

安装编译源码的工具gcc yum install -y gcc下载源码 源码下载地址 https://download.redis.io/releases/ 注意事项 不建议安装最新版本redis&#xff0c;所以我这里选择6.2.6版本 下载 wget https://download.redis.io/releases/redis-6.2.6.tar.gz解压 tar -zxvf redis-…

工业相机和镜头参数和选型

工业相机和镜头参数和选型 文章目录 工业相机和镜头参数和选型前言一、相机参数解释和选型1.相机参数1.1快门-shutter1.2曝光-exposure1.3增益-gain1.4 感光芯片类型&#xff08;CCD/CMOS&#xff09;1.5 感光芯片&#xff08;靶面&#xff09;尺寸1.6 分辨率1.7 像元尺寸1.8 帧…

【点量云流】国内首家适配国产信创的实时云渲染解决方案,助力国产化信创新体验!

一、背景 随着信息技术的广泛应用&#xff0c;信息安全与自主可控成为国家发展的重要保障。近年来&#xff0c;国产化信创的发展&#xff0c;为推动信息技术产业自主创新&#xff0c;实现关键技术和产品的自主可控&#xff0c;对于保障国家信息安全、促进产业发展有着重要意义。…

程序员英语之Spring篇

spring.io/quickstart 本期课程讲解Spring官网的快速上手页面 官网地址 https://spring.io/quickstart Spring Quickstart Guide Spring 快速开始指南 Guide 指南 What you’ll build 接下来你将要构建的是什么&#xff1f; build 构建 You will build a classic “H…

Laravel 6 - 第十二章 控制器

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

计算机网络4——网络层1

文章目录 一、网络层1、概念2、网络层的两个层面1&#xff09;介绍2&#xff09;问题3&#xff09;解决 二、网际协议IP1、介绍2、虚拟互联网络1&#xff09;介绍2&#xff09;案例 3、IP地址1&#xff09;IP 地址及其表示方法2&#xff09;分类的IP地址3&#xff09;无分类编址…

FineVis助力智慧水务:水务可视化智能决策与管理

一、智慧水务是什么 智慧水务是利用数采仪、无线网络、水质水压表等在线监测设备实时感知城市供排水系统的运行状态&#xff0c;以及采用可视化方式整合水务管理部门与供排水设施&#xff0c;形成“城市水务物联网”的创新管理模式。通过这一系统&#xff0c;海量水务信息可以…

慧明咨询:成绩公式,发现未知,助力进步

在当今社会,数以万计的学子面临着激烈的教育竞争。2000万的中考生、1000万的高考生、300万的考研生以及500万的国考备考者,都在为了通过这“独木桥”而战。 面对有限的优质学区、师资力量的不足、以及巨大的心理压力,学生及其家长们焦虑不已。为此,深圳市慧明咨询顾问有限公司…

CentOS-7安装clickhouse并允许其他主机登录

一、通用设置 1、配置主机名 hostnamectl set-hostname --static 主机名2、修改hosts文件 vim /etc/hosts 输入&#xff1a; 192.168.15.129 master 192.168.15.133 node1 192.168.15.134 node2 192.168.15.136 node33、 保持服务器之间时间同步 yum install -y ntpdate &…

【性能测试】ChaosTesting(混沌测试)ChaosBlade(混沌实验工具)(五)-jvm混沌实验

6. chaosblade-jvm实验场景 6.1 挂载 java agent blade prepare jvm 6.1.1 介绍 挂载 java agent&#xff0c;执行 java 实验场景必要步骤 6.1.2 参数 -j, --javaHome string&#xff1a; 指定 JAVA_HOME 路径&#xff0c;用于指定 java bin 和 tools.jar&#xff0c;如果…

《HCIP-openEuler实验指导手册》1.1Apache安装与测试

一、安装httpd 查看软件仓库中apache版本列表 dnf provides http 安装apache dnf install -y httpd 二、启动http并测试 查看apache版本号 httpd -v 检查配置文件是否正确 httpd -t 将如下97行取消注释消除报错 重新测试配置文件 httpd -t 启动并设置为开机启动 syste…

QT中对于QPushButton样式的调整

文章目录 前言1.QPushButton1.1 新建项目导入资源1.2 添加Push Button并定义样式1.3 调整样式1.4 实际需求情况1.5 背景色和边框 2. 一些概念理解2.1 图片2.2 边距 总结 前言 前段时间在调软件的样式&#xff0c;学到了些新的东西&#xff0c;也碰到了些问题&#xff0c;这里做…

光纤、以太网电缆和 DSL 的比较:技术指南

了解光纤、以太网电缆和 DSL 之间的差异对于做出有关互联网连接的明智决策至关重要。本技术指南对这些技术进行了全面比较&#xff0c;讨论了它们的独特功能、性能指标和应用。它旨在为您提供必要的知识&#xff0c;以选择最适合您的特定需求的选项。 光纤、以太网电缆和 DSL …

汇编期末复习知识点

参考文献1 第一章 概述 组成 计算机系统由硬件子系统和软件子系统组成。硬件子系统&#xff1a;组成计算机系统的所有电子的&#xff0c;机械的&#xff0c;光学的和磁性的元部件。 计算机中常用进制数表示 十进制(Decimal):数据尾部加一后缀D&#xff0c;如2355D二进制&a…