27-Servlet执行原理

news/2024/4/24 4:42:48/文章来源:https://blog.csdn.net/WWXDwrn/article/details/130371280

目录

1.Tomcat详解

①接收请求:

②根据请求计算响应:

③返回响应:

2.Tomcat执行流程

2.1.Tomcat 初始化流程

2.2.Tomcat 处理请求流程

2.3.Servlet 的 service 方法的实现


在 Servlet 的代码中并没有写 main ⽅法,那么对应的 doGet 代码是如何被调⽤的呢? 响应⼜是如何返回给浏览器的?这就要从 Tomcat 说起了。

1.Tomcat详解

我们⾃⼰的实现是在 Tomcat 基础上运⾏的。

当浏览器给服务器发送请求的时候,Tomcat 作为 HTTP 服务器,就可以接收到这个请求。HTTP 协议作为⼀个应⽤层协议,需要底层协议栈来⽀持⼯作:

更详细的交互过程:

①接收请求:

  • ⽤户在浏览器输⼊⼀个 URL,此时浏览器就会构造⼀个 HTTP 请求。
  • 这个 HTTP 请求会经过⽹络协议栈逐层进⾏封装成⼆进制的 bit 流,最终通过物理层的硬件设备转换成光信号/电信号传输出去。
  • 这些承载信息的光信号/电信号通过互联⽹上的⼀系列⽹络设备,最终到达⽬标主机(这个过程也需要⽹络层和数据链路层参与)。
  • 服务器主机收到这些光信号/电信号,⼜会通过⽹络协议栈逐层进⾏分⽤,层层解析,最终还原成 HTTP 请求。并交给 Tomcat 进程进⾏处理(根据端⼝号确定进程)。
  • Tomcat 通过 Socket 读取到这个请求(⼀个字符串),并按照 HTTP 请求的格式来解析这个请求,根据请求中的 Context Path 确定⼀个 webapp,再通过 Servlet Path 确定⼀个具体的类。再根据当前请求的⽅法 (GET/POST/...),决定调⽤这个类的 doGet 或者 doPost 等⽅法。此时我们的代码中的 doGet / doPost ⽅法的第⼀个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息。

②根据请求计算响应:

  • 在我们的 doGet / doPost ⽅法中,就执⾏到了我们⾃⼰的代码。我们⾃⼰的代码会根据请求中的⼀些信息,来给 HttpServletResponse 对象设置⼀些属性,例如状态码,header,body 等。

③返回响应:

  • 我们的 doGet / doPost 执⾏完毕后,Tomcat 就会⾃动把 HttpServletResponse 这个我们刚设置好的对象转换成⼀个符合 HTTP 协议的字符串,通过 Socket 把这个响应发送出去。
  • 此时响应数据在服务器的主机上通过⽹络协议栈层层封装,最终⼜得到⼀个⼆进制的 bit 流,通过物理层硬件设备转换成光信号/电信号传输出去。
  • 这些承载信息的光信号/电信号通过互联⽹上的⼀系列⽹络设备,最终到达浏览器所在的主机(这个过 程也需要⽹络层和数据链路层参与)。
  • 浏览器主机收到这些光信号/电信号,⼜会通过⽹络协议栈逐层进⾏分⽤,层层解析,最终还原成 HTTP 响应,并交给浏览器处理。
  • 浏览器也通过 Socket 读到这个响应(⼀个字符串),按照 HTTP 响应的格式来解析这个响应,并且把body 中的数据按照⼀定的格式显示在浏览器的界⾯上。

2.Tomcat执行流程

下⾯的代码通过 "伪代码" 的形式描述了 Tomcat 的"初始化"/"处理请求"两部分核⼼逻辑。

所谓 "伪代码",并不是⼀些语法严谨,功能完备的代码,只是通过这种形式来⼤概表达某种逻辑。

2.1.Tomcat 初始化流程

class Tomcat {// ⽤来存储所有的 Servlet 对象private List<Servlet> instanceList = new ArrayList<>();public static void main(String[] args) {new Tomcat().start();}public void start() {// 根据约定,读取 WEB-INF/web.xml 配置⽂件,并解析被 @WebServlet 注解修饰的类// 假定这个数组⾥就包含了我们解析到的所有被 @WebServlet 注解修饰的类Class<Servlet>[] allServletClasses = ...// 这⾥要做的的是实例化出所有的 Servlet 对象出来for (Class<Servlet> cls : allServletClasses) {// 这⾥是利⽤ java 中的反射特性做的// 实际上还得涉及⼀个类的加载问题,因为我们的类字节码⽂件,是按照约定的⽅式(全部在WEB-INF/classes ⽂件夹下)存放的,所以 tomcat 内部是实现了⼀个⾃定义的类加载器(ClassLoader)⽤来负责这部分⼯作Servlet ins = cls.newInstance();instanceList.add(ins);}//开始方法// 调⽤每个 Servlet 对象的 init() ⽅法,这个⽅法在对象的⽣命中只会被调⽤这⼀次for (Servlet ins : instanceList) {ins.init();}// 利⽤我们之前学过的知识,启动⼀个 HTTP 服务器// 并⽤线程池的⽅式分别处理每⼀个 RequestServerSocket serverSocket = new ServerSocket(8080); //开启一个web服务并设置端口号,监测:若有人访问此ip+端口,是能感知到的// 实际上 tomcat 不是⽤的固定线程池,这⾥只是为了说明情况ExecuteService pool = Executors.newFixedThreadPool(100);//多次拦截调用方法while (true) { //死循环,一直等待别人去访问Socket socket = ServerSocket.accept(); //如果没有人访问,就会阻塞到这行代码;如果有人访问,就会拿到请求的信息,往下执行// 每个请求都是⽤⼀个线程独⽴⽀持,这⾥体现了我们 Servlet 是运⾏在多线程环境下的pool.execute(new Runnable() {doHttpRequest(socket); //包含了一系列method方法});}//销毁方法// 调⽤每个 Servlet 对象的 destroy() ⽅法,这个⽅法在对象的⽣命中只会被调⽤这⼀次for (Servlet ins : instanceList) {ins.destroy();}}
}

小结:

  • Tomcat 的代码中内置了 main ⽅法,当我们启动 Tomcat 的时候,就是从 Tomcat 的 main ⽅法开始执⾏的。
  • 被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取到,并集中管理。
  • Tomcat 通过反射这样的语法机制来创建被 @WebServlet 注解修饰的类的实例。
  • 这些实例被创建完了之后,会点调⽤其中的 init ⽅法进⾏初始化。(这个⽅法是 HttpServlet ⾃带的,我们⾃⼰写的类可以重写 init)
  • 这些实例被销毁之前,会调⽤其中的 destory ⽅法进⾏收尾⼯作。(这个⽅法是 HttpServlet ⾃带的,我 们⾃⼰写的类可以重写 destory)
  • Tomcat 内部也是通过 Socket API 进⾏⽹络通信。
  • Tomcat 为了能同时相应多个 HTTP 请求,采取了多线程的⽅式实现。因此 Servlet 是运⾏在多线程环境下的。

2.2.Tomcat 处理请求流程

class Tomcat {void doHttpRequest(Socket socket) {// 参照我们之前学习的 HTTP 服务器类似的原理,进⾏ HTTP 协议的请求解析,和响应构建HttpServletRequest req = HttpServletRequest.parse(socket);HttpServletRequest resp = HttpServletRequest.build(socket);// 判断 URL 对应的⽂件是否可以直接在我们的根路径上找到对应的⽂件,如果找到,就是静态内容// 直接使⽤我们学习过的 IO 进⾏内容输出if (file.exists()) {// 返回静态内容return;}// ⾛到这⾥的逻辑都是动态内容了// 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条// 最终找到要处理本次请求的 Servlet 对象Servlet ins = findInstance(req.getURL());// 调⽤ Servlet 对象的 service ⽅法// 这⾥就会最终调⽤到我们⾃⼰写的 HttpServlet 的⼦类⾥的⽅法了try {ins.service(req, resp);    } catch (Exception e) {// 返回 500 ⻚⾯,表示服务器内部错误}}
}

小结:

  • Tomcat 从 Socket 中读到的 HTTP 请求是⼀个字符串,然后会按照 HTTP 协议的格式解析成⼀个 HttpServletRequest 对象。
  • Tomcat 会根据 URL 中的 path 判定这个请求是请求⼀个静态资源还是动态资源,如果是静态资源,直接找到对应的⽂件,把⽂件的内容通过 Socket 返回。如果是动态资源,才会执⾏到 Servlet 的相关逻辑。
  • Tomcat 会根据 URL 中的 Context Path 和 Servlet Path 确定要调⽤哪个 Servlet 实例的 service ⽅法。
  • 通过 service ⽅法,就会进⼀步调⽤到我们之前写的 doGet 或者 doPost。

2.3.Servlet 的 service 方法的实现

class Servlet {public void service(HttpServletRequest req, HttpServletResponse resp) {String method = req.getMethod();if (method.equals("GET")) {doGet(req, resp);} else if (method.equals("POST")) {doPost(req, resp);} else if (method.equals("PUT")) {doPut(req, resp);} else if (method.equals("DELETE")) {doDelete(req, resp);}......}
}

小结:

  • Servlet 的 service ⽅法内部会根据当前请求的⽅法,决定调⽤其中的某个 doXXX ⽅法。
  • 在调⽤ doXXX ⽅法的时候,就会触发多态机制,从⽽执⾏到我们⾃⼰写的⼦类中的 doXXX ⽅法。

理解此处的多态

  • 我们⾃⼰写的 HelloServlet 类,继承⾃ HttpServlet 类,⽽ HttpServlet ⼜继承⾃ Servlet,相当于 HelloServlet 就是 Servlet 的⼦类。
  • 接下来,在 Tomcat 启动阶段,Tomcat 已经根据注解的描述,创建了 HelloServlet 的实例,然后把这个实例放到了Servlet 数组中。
  • 后⾯我们根据请求的 URL 从数组中获取到了该 HelloServlet 实例,但是我们是通过 Servlet ins 这 样的⽗类引⽤来获取到 HelloServlet 实例的。
  • 最后,我们通过 ins.doGet() 这样的代码调⽤ doGet 的时候,正是 "⽗类引⽤指向⼦类对象",此时就会触发多态机制,从⽽调⽤到我们之前在 HelloServlet 中所实现的 doGet ⽅法。

等价代码:

Servlet ins = new HelloServlet();
ins.doGet(req, resp);

小结:

①Tomcat的main方法

启动Socket网络编程->得到所有请求

②Tomcat的doHttpRequest方法

url->@WebServlet类

Servlet->service(req, resp)

③Servlet的service方法

得到方法类型。

 

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

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

相关文章

【C++关联容器】map的成员函数

目录 map 1. 构造、析构和赋值运算符重载 1.1 构造函数 1.2 析构函数 1.3 赋值运算符重载 2. 迭代器 3. 容量 4. 元素访问 5. 修改器 6. 观察者 7. 操作 8. 分配器 map map是关联容器&#xff0c;它按照特定的顺序存储由关键字值和映射值的组合形成的元素。 在一…

【Springboot系列】项目启动时怎么给mongo表加自动过期索引

1、前言 在之前操作mongo的过程中&#xff0c;都是自动创建&#xff0c;几乎没有手动创建过表。 这次开发中有张表数据量大&#xff0c;并且不是特别重要&#xff0c;数据表维护一个常见的问题是过期数据没有被及时清理&#xff0c;导致数据量过大&#xff0c;查询变得缓慢。…

LeetCode-242. 有效的字母异位词

题目链接 LeetCode-242. 有效的字母异位词 题目描述 题解 题解一&#xff08;Java&#xff09; 作者&#xff1a;仲景 首先&#xff0c;满足条件的情况下&#xff0c;两个字符串的长度一定是相等的&#xff0c;不相等一定不满足条件 使用Hash表来存储字符串s中各个字符出现的…

Spring Security实战(九)—— 使用Spring Security OAuth实现OAuth对接

一、OAuth2.0介绍 OAuth2.0是一种授权协议&#xff0c;允许用户授权第三方应用程序代表他们获取受保护的资源&#xff0c;如个人信息或照片等。它允许用户授权访问他们存储在另一个服务提供商上的资源&#xff0c;而无需将其凭据共享给第三方应用程序。OAuth2.0协议建立在OAuth…

【具体到每一步】从0制作一个uniapp的新闻类页面(界面篇)

目录 项目初始化 / 基础配置 项目创建 配置路由/页面/tabbar pages.json配置tabbar 配置图标/静态资源 导航栏和字体颜色 scroll-view实现横向滚动条样式 公共模块定义components组件 新建组件 使用组件 组件里的结构 布局个人中心页面 组件差异化处理 数据传递 导航…

DevExpress:报表在winform窗体上显示(使用documentViewer控件)

一&#xff1a;控件认识 documentViewer&#xff08;版本DX22.2&#xff09;,老版本中的可能是printControl&#xff08;工具箱面板中可能找不到&#xff09;&#xff0c;通过官网搜索发现&#xff0c;这个控件现在继承于documentViewer这个控件。因此&#xff0c;使用documen…

Unity入门(一)

Unity Unity是一套完善体系与编辑器的跨平台游戏开发工具&#xff0c;也可以称之为游戏引擎。游戏引擎是指一些编写好的可以重复利用的代码与开发游戏所用的各功能编辑器。 基于C#编程&#xff0c;易上手&#xff0c;高安全性独特的面向组件游戏开发思想让游戏开发更加简单易…

【神经网络】tensorflow实验7--回归问题

1. 实验目的 ①掌握一元线性回归模型的实现方法 ②掌握多元线性回归模型的实现方法 ③掌握三维数据可视化方法 2. 实验内容 ①使用TensorFlow建立一元线性回归模型&#xff0c;使用商品房销售数据训练模型&#xff0c;并使用训练好的模型预测房价 ②使用TensorFlow建立多元线…

十、ElasticSearch 实战 - 源码运行

一、概述 想深入理解 Elasticsearch&#xff0c;了解其报错机制&#xff0c;并有针对性的调整参数&#xff0c;阅读其源码是很有必要的。此外&#xff0c;了解优秀开源项目的代码架构&#xff0c;能够提高个人的代码架构能力 阅读 Elasticsearch 源码的第一步是搭建调试环境&…

思维导图从入门到大神

思维导图怎么做&#xff1f;思维导图是一种发散性思维的图。在我们生活的方方面面都有运用。无论是工作、学习、还是生活&#xff0c;我们都可以用到它。那思维导图是怎么绘制的呢&#xff1f;其实非常简单&#xff0c;只要这简单的几步 1、首先在绘制思维导图前&#xff0c;我…

【网络】-- UDP协议

目录 传输层 再谈端口号 端口号范围划分 认识知名端口号&#xff08;Well-Know Port Number&#xff09; 两个问题 netstat pidof UDP协议 UDP的特点 UDP的缓冲区 UDP使用注意事项 基于UDP的应用层协议 传输层 负责数据能够从发送端传输接收端。 再谈端口号 端…

Codeforces Round 861 (Div. 2)(A~D)

A. Lucky Numbers 给出边界l和r&#xff0c;在区间[l, r]之间找到幸运值最大的数字。一个数字的幸运值被定义为数位差的最大值&#xff0c;即数字中最大的数位和最小的数位的差。 思路&#xff1a;因为涉及到至少两位&#xff0c;即个位和十位变化最快&#xff0c;最容易得到相…

07 - 进程创建大盘点

---- 整理自狄泰软件唐佐林老师课程 查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;Linux系统编程训练营 - 目录 文章目录 1. 进程创建回顾2. 再论进程创建2.1 思考2.2 vfork()深度分析2.3 vfork()要点分析2.4 fork()的现代优化2.5 编程实验&#xff1a;fork() &…

被遗忘的Java关键字:transient

前言 今天在看项目代码时候&#xff0c;看到了下面这样一行代码&#xff0c;用transient修饰了一个变量&#xff0c;主要作用是做一个全局开关。说实话我是第一次看到这个关键字。激发了我的好奇心&#xff0c;所以就了解一下这是何方神圣。 /*** 全局开关*/public static tran…

最新研究:可审计的具有拜占庭鲁棒的联邦学习方案

本人新论文&#xff0c;可免费下载&#xff1a;https://download.csdn.net/download/liangyihuai/87727720 Y. Liang, Y. Li and B. -S. Shin, “Auditable Federated Learning With Byzantine Robustness,” in IEEE Transactions on Computational Social Systems, doi: 10.…

浅谈拉格朗日插值法

浅谈拉格朗日插值法 好像FFT要用到&#xff0c;所以就学习一手 文章目录 浅谈拉格朗日插值法什么是插值拉格朗日插值法 什么是插值 在离散数据的基础上补插连续的函数&#xff0c;使得这条连续函数经过所有离散数据点&#xff0c;这个过程就叫插值。其意义在于&#xff1a; …

论文阅读:DLME = Deep Local-flatness Manifold Embedding

Author: Zelin Zang, Siyuan Li, Di Wu and Stan Z Li. 1-4: Westlake University 摘要 流形学习&#xff08;ML, Manifold learning&#xff09;旨在从高维数据中识别低维结构和嵌入&#xff0c;然而我们发现现有工作在采样不足的现实数据集上效果不佳。一般的ML方法对数据结…

LNMP网站框架搭建

1. Nginx的工作原理 php-fpm.conf 是控制php-fpm守护进程的 php.ini是php解析器 工作进程&#xff1a; 1.客户端通过域名进行请求访问时&#xff0c;会找Nginx对应的虚拟主机 2. Nginx对该请求进行判断&#xff0c;如果是静态请求,Nginx会自行处理&#xff0c;并将处理结果返…

【C++】了解设计模式、 stackqueue的使用与模拟实现

文章目录 1.设计模式2.stack1.stack的使用1.stack的结构2.stack的接口 2.stack的模拟实现1.stack的结构2.接口实现 3.queue1.queue的使用1.queue的结构3.queue的接口 2.queue的模拟实现1.queue的结构2.接口实现 4.了解deque1.deque的原理介绍2.deque的底层结构3.deque的迭代器设…

【Android入门到项目实战-- 7.1】—— 如何使用通知?

目录 一、创建通知的步骤 1、创建一个NotificationManager实例 2、使用一个Builder构造器来创建Notification对象 3、设置标题、文字、时间和图标等信息 4、显示通知 二、通知实例演示 三、实现通知的点击效果 1、PendingIntent 什么是PendingIntent&#xff1f; 如何使…