面试官:听说你很懂SpringMVC,那讲讲其内部对于请求的处理吧!

news2024/10/1 19:55:41

本系列文章皆在分析SpringMVC的核心组件和工作原理,让你从SpringMVC浩如烟海的代码中跳出来,以一种全局的视角来重新审视SpringMVC的工作原理.


作者:毅航

在过去的很长一段时间内,笔者对SpringMVC中的核心控制器DispatcherServletdoDispatch的处理逻辑进行了详细的分析,并以doDispatch的执行逻辑为切入点,详细的拆解了SpringMVC中的MultipartResolve(上传请求处理器)、HandlerMapping(处理器映射器)、HandlerInterceptor(拦截器)、HttpMessageConverter(消息转换器)等组件信息。更详细的内容可参考专栏:SpringMVC流程分析[1]。

在专栏的前几章中我们主要介绍了SpringMVC内部在处理Http请求过程中会需要一些组件,毫无疑问这些组件在处理Http请求过程中发挥着至关重要的作用,但某种程度上来说其可能有一些太过于细节,读完后你可能只是搞懂了 SpringMVC中某些组件的作用以及作用的时机。 同时,你可能会面临这样一个问题,就是 似乎自己搞懂了很多SpringMVC组件,但却很难将其串起来,仿佛每个知识都是孤立的。

针对这一问题,笔者今天想从更加宏观角度来谈一谈一个HTTP请求在SpringMVC内部处理的全过程。以方便读者更加深入理解专栏SpringMVC流程分析[2]的内容,所以本文可以算作是对前几章内容的一个回顾和总结

1前言

在当下这个时代,我们每天都会借助浏览器浏览很多内容。但你是否有考虑过当你在浏览器中访问某一个网址时候,这背后都发生了那些事情呢?

事实上,当在浏览器中键入url后,其背后的处理逻辑可大致如下图所示:

可以看到,键入url后其大致会经历如下几个步骤:

  1. DNS解析: 首先,浏览器会解析网址中的主机名,以获取服务器的IP地址。这个过程通过DNS(域名系统)完成。

  2. 建立TCP连接: 一旦浏览器知道了服务器的IP地址,它会尝试建立到服务器的TCP连接。通常这个过程会包括三次握手,以确保客户端和服务器之间的连接建立成功。

  3. 发送HTTP请求: 一旦TCP连接建立,浏览器会发送一个Http请求到服务器。这个请求包括了要访问的资源路径、HTTP方法(GET、POST等)、请求头(包含用户代理、接受的数据类型等)以及任何附加数据(例如,表单数据或请求体)。

  4. 服务器处理请求: 服务器收到HTTP请求后,会根据请求的路径和方法来确定如何处理请求。这通常涉及到后端应用程序的控制器(例如,SpringMVC的Controller)处理请求,执行相应的业务逻辑。

  5. 生成HTTP响应,发送响应: 一旦服务器完成请求处理,它会生成一个Http响应。而这个响应通常包括:状态码、响应头以及响应体

  6. 浏览器渲染: 浏览器接收到HTTP响应后,根据响应的内容类型(例如,HTML、CSS、JavaScript等)来渲染页面。

实际上Http请求的处理流程会涉及到很多复杂细节,笔者在此也只是做一个抛砖引玉的介绍,其主要目的也是在于让读者对于Http请求的大致处理过程有一个大致的认识。

2简化Http请求的处理流程

虽然上述已经对Http请求的处理流程进行了极大的简化,但可能你还是会觉得上述对于Http请求处理的有些繁琐。那能不能对上述步骤再进行一层抽象,让其更容易理解呢?答案当然是肯定的!

基于此,我们对上图所示的Http请求处理流程进行再一次抽象,得到下图所示内容:

可以看到当对复杂事物进行抽象后,一切似乎好像开始变得简单了。接下来,我们大致来分析下图中的大致流程:

  1. 浏览器发起Http请求至中间件,这个中间件可以概括很多东西。比如:反向代理,路由跳转、网络分发等。

  2. Http请求到达后端服务器Tomcat,其实你可以将Tomcat理解为一个接待请求Http请求的接待员,其主要负责统筹处理请求,而不是所有的事情都自己做。进一步,其会将处理逻辑委托给进程

为了进一步方便理解,我们举一个网购的例子来分析Http请求处理的大致逻辑。首先,当我们在浏览器键入发起购物的Http请求后,这个Http请求在不丢包的前提下,最终会历经万水千山到达Tomcat;然后,Tomcat会将Http请求分发给专门管理订单的进程,并记录其对于某个商品的购买数量。此外,还需要告诉管理库存的进程,进行库存的扣减,同时,还要告诉支付的进程,应该支付的金额。最后,在将处理结果返回给浏览器进行解析。

可以看到Tomcat会将Http请求分发给专门的进程来处理Http请求,而具体到Java后端搭建的服务来看,其会将Http请求交给交给SpringMVC来进行处理。

其实讲了这么多,重点在于分析清楚Http请求是如何到达后端服务的;在此基础上,我们重点关注后端服务SpringMVC内部是如何来完成对Http请求的处理。

3SpringMVC内部对于Http请求的处理

在开始分析之前,我们先来看看后端服务是如何来定义前端待访问的url的:

@RestController
@RequestMapping("/demo")
public class HelloWorldController {

 @GetMapping(value="/say-hello")
 public String sayHello(){
     return "helloworld";
  }
}

观察上述代码你会发现,其主要做了如下工作:

  1. 定义了一个待访问的url信息,即暴露给前端的地址信息为/demo/say-hello

  2. url的请求方式为Get方式;

  3. url对应的处理逻辑为sayHello方法。

明白了这些后,不妨思考一个问题,如果你来处理前端传来的/demo/say-hello请求,你该如何处理? 我想你的思路大致如下:

  1. 获取请求的url信息;

  2. 根据url信息获取对应的处理逻辑,具体到SpringMVC中即寻找url对应的方法信息;

  3. 处理请求中的参数信息,执行url中对应的方法,并将结果返回。

进一步,将上述逻辑翻译为代码即有如下内容:

public class HttpRequestHandler {
    
    Map<RequestUrl, Method> mapper = new HashMap<>();
    
    
    * 处理前端传来的请求信息
    */
    public Object handle(HttpRequest httpRequest {
        // 获取前端传来的url信息
        RequestUrl requestKey = getRequestUrl(httpRequest);
        
        Method method = this.mapper.getValue(requesUrl);
       
       Object[] args = resolveArgsAccordingToMethod(httpRequest, method);
       
      return method.invoke(controllerObject, args);
}

事实上,这也就是SpringMVC中处理Http请求的底层了逻辑。虽然上述代码中省略了很多方法的编写,但这并不影响理解。

熟悉了SpringMVC处理Http请求的底层逻辑后,我们再来进一步看看SpringMVC在处理Http请求时的具体细节

4Http请求从Tomcat转交到SpringMVC控制器

我们首先来看Tomcat内部是如何将对Http请求的处理权转交给SpringMVC的。

众所周知,对于 SpringBoot应用而言,其本身并不提供通信层的支持,它是依赖于 Tomcat、Jetty 等容器来完成通信层的支持。而在SpringBoot应用中Tomcat是如何被启动的我们在此便不再赘述了,具体可参考: 一文讲透SpringBoot应用在内嵌Tomcat容器与外置容器下的启动原理[3]。

进一步,当Tomcat启动后,此时遇到HTTP 请求访问,其会触发 Tomcat 底层提供的 NIO 通信来完成数据的接收,并最终将请求事件丢入线程池去处理,进而根据servlet-mapping配置的url-pater来选取对应的Servlet来进行处理。

这就是为什么使用SpringMVC时,需要将DispatcherServlet中的url-pattern改为/*拦截所有请求的原因。 而有关DispatcherServlet的相关介绍可参考:SpringMVC流程分析(二):揭开DispatcherServlet的神秘面纱[4]。

5SpringMVC控制器对于请求的处理

进一步,当url请求到达DispatcherServlet后,最终会调用到其中的doDispatch方法。而这个方法我们之前曾具体分析过,其大致处理逻辑如下:

这个过程具体会涉及到哪些处理逻辑,在此我们在此就不再赘述了。但其总结来看无非完成如下逻辑:

  1. 分发,即根据请求寻找对应的执行方法。 这个过程主要涉及到url处理器之间的匹配。这一过程中会涉及到的组件有:HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)等。

  2. 执行,反射执行寻找到的执行方法。 这一过程的本质就是通过反射机制来调用被@ReqeustMapping标注的方法

进一步,在SpringMVC流程分析(六):为处理器进行合适的“适配”[5]我们曾提到过:“对于前端传来的url信息,最终会交由RequestMappingHandlerAdapter中的handleInternal来进行处理”。更进一步,其本质会将方法反射执行的逻辑委托给ServletInvocableHandlerMethod来处理,其相关调用链如下:

(注:SpringMVC内部这块通过反射执行方法的逻辑相对来说还是比较复杂的,由于笔者自身水平有限,所以在之前分析中一笔带过,感兴趣的读者可私下自行其内部执行逻辑,笔者期待你的佳作~~~)

6总结

最后,我们来回顾下本文的内容。首先,我们从Http请求的处理过程入手,详细讨论了Http请求处理的全过程。以此为基础,我们重点分析了在后端服务中Http请求在SpringMVC中处理的全过程。总结来看SpringMVC在处理前端传来的url请求时,主要完成了两项任务:

  1. 请求分发,即根据url寻找对应的处理器;

  2. 方法执行,即执行与对应url绑定的方法信息,并将执行结果返回到客户端。

进一步,SpringMVC内部对于Http请求的处理过程如下所示:

这张图是笔者从网上找的,上述的ViewResolver、View其实现在已经很少用了。更多的还是在处处理器上使用@RestController或者@ResponseBody注解,以指示返回的数据应该以JSON格式响应给客户端。而完成这一工作的组件即为我们之前介绍的HttpMessageConverter,有关HttpMessageConverter相关介绍可参考:SpringMVC流程分析(七):HttpMessageConverter——SpringMVC中的消息转换[6]。

其实细心的读者已经注意到了,笔者在介绍SpringMVC内部对于Http请求的处理逻辑时,并没有其他文章一样以上述这样为基础来向写流水账一样的告诉读者SpringMVC内部处理Http请求会经历那些过程,然后使用那些组件。

这样的介绍在笔者看来其只是告诉了一个零散的知识点,即SpringMVC内部对于Http请求的处理。而这与你之前学到的知识是割裂的,其相互无法产生直接的联系。这导致最直接的后果就是你一直在不断的学习,看似学了很多但其实很多时候其实都在做无用功。

所以笔者更希望在介绍知识时能将当前知识点与之前所需知识串联起来,进而将零碎的知识点串联起来,这样才能授人以鱼的同时授人以渔,进而当日后遇到相似问题时也才能举一反三。

如果觉得笔者文章对你很有启发的话,不妨点赞、收藏、关注三连走起,以免错过日后的每一次更新。

参考资料

[1]

https://juejin.cn/column/7256779530392256567: https://juejin.cn/column/7256779530392256567

[2]

https://juejin.cn/column/7256779530392256567: https://juejin.cn/column/7256779530392256567

[3]

https://juejin.cn/post/7283328111504293942: https://juejin.cn/post/7283328111504293942

[4]

https://juejin.cn/post/7258165891279568951: https://juejin.cn/post/7258165891279568951

[5]

https://juejin.cn/post/7263840667825782821: https://juejin.cn/post/7263840667825782821

[6]

https://juejin.cn/post/7265218003863420982: https://juejin.cn/post/7265218003863420982

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

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

相关文章

《鱿鱼游戏》杀入体验店 SandboxVR未能复制神话

曾经火爆全网的剧集《鱿鱼游戏》这回真的变成游戏了&#xff0c;而且还是能身临其境去玩的那种。 9月底&#xff0c;沉浸式游戏社交公司Sandbox VR与全球知名流媒体Netflix合作&#xff0c;将《鱿鱼游戏》搬入VR体验店&#xff0c;用线下场地VR的方式&#xff0c;重现123木头人…

jvm--执行引擎

文章目录 1. 执行引擎的工作流程2. 解释器、JIT及时编译器3. 热点代码及探测技术4. HotSpotVM 中 JIT 分类 执行引擎属于 JVM 的下层&#xff0c;里面包括解释器、及时编译器、垃圾回收器 JVM 的主要任务是负责 装载字节码到其内部&#xff0c;但字节码并不能够直接运行在操作…

vite+vue3+ts中使用require.context | 报错require is not defined | 获取文件夹中的文件名

vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名 目录 vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名一、问题背景二、报错原因三、解决方法 一、问题背景 如题在vitevue3ts中使用required.context时报…

JDK21 要来了,,你不会还在用 Java 8吧!

目前 Java 的最新稳定版是 JDK 20&#xff0c;但这是个过渡版&#xff0c;JDK21就是 LTS 版的了&#xff0c;也快要发布了&#xff0c;在今年9月份&#xff08;也就是2023年9月&#xff09;就要正式发布了。 但是&#xff0c;猜都不用猜&#xff0c;你肯定还在用 Java 8 吧&…

【密评】商用密码应用安全性评估从业人员考核题库(七)

商用密码应用安全性评估从业人员考核题库&#xff08;七&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 1501 判断题 在公钥密码体制中&#xff0c;使用接收方的公钥加密的消…

MySQL通用查询日志 general query log 详解

通用查询日志&#xff08;general query log&#xff09;用来记录用户的所有操作&#xff0c;包括启动和关闭MySQL服务、所有用户的连接开始时间和截止时间、发送给MySQL数据库服务器的所有SQL指令等。当我们的数据发生异常时&#xff0c;查看通用查询日志&#xff0c;还原操作…

Oracle笔记-对ROWNUM的一次理解(简单分页)

此博文记录时间&#xff1a;2023-05-05&#xff0c;发到互联网上是2023-10-09 这个在分页里面用得比较多&#xff0c;在MySQL中&#xff0c;通常使用limit去操作&#xff0c;而去感觉比较简单&#xff0c;Oracle中无此关键字。 通过查阅资料后&#xff0c;要实现分页需要用到…

论文阅读笔记(Clover: 计算与存储被动分离的分布式键值存储系统)

关于Disaggregating Persistent Memory and Controlling Them Remotely: An Exploration of Passive Disaggregated Key-Value Stores这篇论文的笔记 原文链接 提出背景 传统的分布式存储系统中&#xff0c;每个节点都会包含计算和存储两个部分&#xff0c;一个节点既可以访…

分割等和子集

题目链接 分割等和子集 题目描述 注意点 数组 nums 非空数组 nums 只包含正整数 解答思路 最初想到的是根据回溯剪枝解决本题&#xff0c;如果数组大小小于2&#xff0c;则肯定不能找到分割等和子集&#xff0c;除此以外&#xff0c;如果数组之和sum不能被2整除&#xff0…

VMvare虚拟机安装国产麒麟V10桌面操作系统

一、系统下载 进入银河麒麟官网&#xff1a;https://www.kylinos.cn/ 选择桌面操作系统&#xff0c;然后进入操作系统版本选择页面&#xff0c;选择银河麒麟桌面操作系统V10 选择后&#xff0c;进入系统介绍页面&#xff0c;然后点击申请试用 点击后进入申请页面&#xf…

Apache Solr9.3 快速上手

Apache Solr 简介 Solr是Apache的顶级开源项目&#xff0c;使用java开发 &#xff0c;基于Lucene的全文检索服务器。 Solr比Lucene提供了更多的查询语句&#xff0c;而且它可扩展、可配置&#xff0c;同时它对Lucene的性能进行了优化。 安装 下载 : 下载地址解压 : tar -zxv…

uniapp apple 苹果登录 离线本地打包

官方文档 uni-app官网 文档写的不全&#xff0c;没有写离线打包流程 加lib 签名里带 sign in with apple hbuilder开关 代码 测试代码&#xff0c;获取app里所有的provider uni.getProvider({service: oauth,success: function (res) {console.log(res.provider)uni.showT…

ctfshow-web4(文件包含日志注入)

像web3那样使用php伪协议&#xff0c;回显error 看了看提示&#xff1a;日志注入 文件包含 使用Wappalyzer查看一下&#xff0c;使用的中间件是Ngnix 日志包含漏洞的成因还是服务器没有进行严格的过滤 &#xff0c;导致用户可以进行任意文件读取&#xff0c; 但是前提是服务器…

月报总结|Moonbeam 9月份大事一览

相信社区的小伙伴都过了一个圆满的中秋节&#xff0c;月圆人团圆&#xff0c;以月光代表生态的Moonbeam网络也如月亮一般&#xff0c;从始至终都在波卡。 9月&#xff0c;USDC上线波卡增加流动性、Moonbeam治理界面简化促进社区参与、Grant发放引爆Moonbeam Ignite再次回归、M…

QT基础入门——Qt事件(五)

前言&#xff1a; 事件&#xff08;event&#xff09;是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘&#xff0c;或者是窗口需要重新绘制的时候&#xff0c;都会发出一个相应的事件。一些事件在对用户操作做出响应时发出&#xff0c;如键盘事件等&#x…

(一)Apache log4net™ 手册 - 介绍

0、相关概念 Log4j 几乎每个大型应用程序都包含自己的日志记录或跟踪 API。根据这一规则&#xff0c;E.U. SEMPER &#x1f339;项目决定编写自己的跟踪 API。那是在 1996 年初。经过无数次的增强、几个化身和大量的工作&#xff0c;API 已经发展成为 log4j —— 一个流行的 Ja…

开源音乐播放器!

导读音乐是生活的一部分。维基百科关于音乐发展历史的文章有这样一段不错的描述说&#xff1a;“全世界所有的人们&#xff0c;包括哪怕是最孤立、与世隔绝的部落&#xff0c;都会有自己的特色音乐……”好吧&#xff0c;我们开源人就构成了一个部落。我建议我们的“音乐形式”…

halcon 算子shape_trans

shape_trans 函数原型&#xff1a;shape_trans(Region : RegionTrans : Type : ) 函数作用&#xff1a;变换区域的形状 参数列表&#xff1a; Region&#xff08;in&#xff09;&#xff1a;被变换的区域 RegionTrans&#xff08;out&#xff09;&#xff1a;变换后的区域…

【SmartApi】内网局域网API调试开发工具下载使用说明-v1.0.1

最近的一些年经常在内网环境下进行开发工作&#xff0c;尤其是比较敏感的单位或者组织是有严格规定不能使用哪些软件或者是不能访问外网的。 但开发工作需要的各种软件工具又因为一些营销策略而要联网激活&#xff0c;这就和组织单位的要求相悖了。于是就萌发了开发内网局域网软…

Ajax跨域访问,访问成功但一直走error不走success的的问题解决

Ajax跨域访问,访问成功但一直走error不走success的的问题解决 通过搜索各种资料&#xff0c;终于解决啦&#xff0c;废话不多说了&#xff0c;还是老规矩直接上代码&#xff1a; 我这里用了jsonp&#xff0c;有想了解的点击 : jsonp 前端代码&#xff1a; $.ajax({type:post…