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;// 通过视图解析器解析返回视图对象ViewView 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);}}这一步牵连到的类有哪些:HandlerExecutionChainHandlerMethodHandlerInterceptorHandlerMappingRequestMappingHandlerMapping(是HandlerMaping接口的实现)

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

分析:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());1. 底层使用了适配器模式。2. 每一个处理器(我们自己写的Controller),都有自己适合的处理器适配器。3.SpringMVC当中处理器适配器也有很多种,其中一个比较有名的,常用的处理器适配器是:RequestMappingHandlerAdapter
这个处理器适配器是专门处理 “处理器方法”上有 @RequestMapping 注解的。4. mappedHandler.getHandler() 获取的是 HandlerMethod 对象5. HandlerAdapter也是一个接口:其中有一个常用的实现类:RequestMappingHandlerAdapter6. 在服务器启动阶段,所有的 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;// 通过视图解析器解析返回视图对象ViewView 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.rhkb.cn/news/314648.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity类银河恶魔城学习记录15-5,6 p157 Audio time limiter p158 Area sound

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili​​ AreaSound.cs using System.Collections; using System.Collections.G…

精益生产咨询公司能够为浙江企业带来哪些帮助?

精益生产&#xff0c;起源于丰田生产方式&#xff0c;强调以最少的资源投入获得最大的运营效益。其核心思想包括消除浪费、持续改进、员工参与和顾客至上。在浙江这片民营经济繁荣的土地上&#xff0c;众多企业敏锐地捕捉到了精益生产带来的巨大潜力&#xff0c;积极寻求与咨询…

SpringBoot学习之Kafka发送消费消息入门实例(三十五)

使用Kafka之前需要先启动fKafka,如何下载安装启动kafka请先参考本篇文章的前两篇: 《SpringBoot学习之Kafka下载安装和启动【Windows版本】(三十四)》 《SpringBoot学习之Kafka下载安装和启动【Mac版本】(三十三)》 一、POM依赖 1、加入kafka依赖 2、我的整个POM代码…

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测 目录 分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类…

怎么通过Javascript脚本实现远程控制一路开关

怎么通过Javascript脚本实现远程控制一路开关呢&#xff1f; 本文描述了使用Javascript脚本调用HTTP接口&#xff0c;实现控制一路开关。一路开关可控制一路照明、排风扇等电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称1智能WiFi…

Spark-机器学习(3)回归学习之线性回归

在之前的文章中&#xff0c;我们了解我们的机器学习&#xff0c;了解我们spark机器学习中的特征提取和我们的tf-idf&#xff0c;word2vec算法。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你…

从头开始构建自己的 GPT 大型语言模型

图片来源&#xff1a; Tatev Aslanyan 一、说明 我们将使用 PyTorch 从头开始构建生成式 AI、大型语言模型——包括嵌入、位置编码、多头自注意、残差连接、层归一化&#xff0c;Baby GPT 是一个探索性项目&#xff0c;旨在逐步构建类似 GPT 的语言模型。在这个项目中&#xff…

java 学习一

jdk下载地址 配置环境变量

网络安全实训Day24(End)

写在前面 并没有完整上完四个星期&#xff0c;老师已经趁着清明节假期的东风跑掉了。可以很明显地看出这次持续了“四个星期”实训的知识体系并不完整&#xff0c;内容也只能算是一次基础的“复习”。更多的内容还是靠自己继续自学吧。 网络空间安全实训-渗透测试 文件包含攻击…

机器人系统开发ros2-基础实践01-学会自定义一个机器人动作aciton实体类

您之前在了解操作教程中了解了action 。与其他通信类型及其各自的接口&#xff08;主题/消息和服务/srv&#xff09;一样&#xff0c;您也可以在包中自定义操作。本教程向您展示如何定义和构建可与您将在下一个教程中编写的action服务器和action 客户端一起使用的操作。 需要理…

RabbitMQ发布确认和消息回退(6)

概念 发布确认原理 生产者将信道设置成 confirm 模式&#xff0c;一旦信道进入 confirm 模式&#xff0c;所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始)&#xff0c;一旦消息被投递到所有匹配的队列之后&#xff0c;broker就会发送一个确认给生产者(包含消…

GDPU 竞赛技能实践 天码行空9

1. 埃式筛法 求区间[2, n]内所有的素数对 &#x1f496; Main.java import java.util.Scanner;public class Main {static int N (int) 1e8, cnt 0;static int[] p new int[N];static boolean[] st new boolean[N];public static void main(String[] args){Scanner sc …

(mac)Promethues监控之mysqld_exporter(MySQL监控)

搭建Mysqld_exporterPrometheusGrafana监控系统 普罗米修斯是后端数据监控平台&#xff0c;通过Mysqld_exporter收集mysql数据&#xff0c;Grafana将数据用图形的方式展示出来 前提&#xff1a;已安装grafana和promethues 1.下载安装Mysql &#xff08;1&#xff09;启动MySQL…

一个联合均值与方差模型的R包——dglm

目录 一、引言二、包的安装与载入三、模拟例子3.1 数据生成3.2 数据查看3.3 模型估计参数 一、引言 在 R 语言中&#xff0c;dglm 包是用于拟合双参数广义线性模型&#xff08;Double Generalized Linear Models&#xff0c;简称 DGLMs&#xff09;的一个工具。这类模型允许同…

工厂高温如何降温?

工厂高温降温的方法有多种&#xff0c;以下是一些常见且有效的策略&#xff1a; 使用风扇或工业大风扇&#xff1a;风扇能够加速空气流动&#xff0c;使人体表面的汗液蒸发速度加快&#xff0c;从而带走更多的热量&#xff0c;实现降温效果。工业大风扇是小型风扇的升级产物&a…

go语言实现简单登陆返回token样例

目录 1、代码实现样例&#xff1a; 2、postman调用&#xff0c;获取登陆后的token&#xff1a; 1、代码实现样例&#xff1a; package mainimport ("net/http""time""github.com/dgrijalva/jwt-go""github.com/gin-gonic/gin" )var …

【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序(万字博文)

系列文章目录 【网络通信基础】网络中的常见基本概念 【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序&#xff08;万字博文&#xff09; 【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制&#xff08;CRC算法、MD5算法&#xff09; 文章目…

密码学 | 承诺:绑定性 + 隐藏性

&#x1f951;原文&#xff1a;承诺方案&#xff08;Commitment&#xff09;学习笔记 &#x1f951;写在前面&#xff1a; 本文属搬运博客&#xff0c;自己留存学习。本文只会讲承诺的两个安全属性&#xff0c;不会再讲解承诺的定义。 正文 承诺方案需要满足两个安全属性&…

C++笔记:C++中的重载

重载的概念 一.函数重载 代码演示例子&#xff1a; #include<iostream> using namespace std;//函数名相同&#xff0c;在是每个函数的参数不相同 void output(int x) {printf("output int : %d\n", x);return ; }void output(long long x) {printf("outp…

手撕netty源码(一)- NioEventLoopGroup

文章目录 前言一、NIO 与 netty二、NioEventLoopGroup 对象的创建过程2.1 创建流程图2.2 EventExecutorChooser 的创建 前言 processOn文档跳转 本文是手撕netty源码系列的开篇文章&#xff0c;会先介绍一下netty对NIO关键代码的封装位置&#xff0c;主要介绍 NioEventLoopGro…