【Tomcat与网络2】一文理解Servlet是怎么工作的

在前面,我们研究了如何用idea来启动一个Servlet程序,今天我们就再来看一下Servlet是如何工作的。

目录

1.Servlet 介绍

2.Servlet 容器工作过程

3.Servlet的扩展


不管是电脑还是手机浏览器,发给服务端的就是一个 HTTP 格式的请求,HTTP 服务器收到这个请求后,需要调用服务端程序来处理,所谓的服务端程序就是你写的 Java 类,一般来说不同的请求需要由不同的 Java 类来处理。

那么问题来了,HTTP 服务器怎么知道要调用哪个 Java 类的哪个方法呢。最直接的做法是在 HTTP 服务器代码里写一大堆 if else 逻辑判断:如果是 A 请求就调 X 类的 M1 方法,如果是 B 请求就调 Y 类的 M2 方法。

但这样做明显有问题,因为 HTTP 服务器的代码跟业务逻辑耦合在一起了,如果新加一个业务方法还要改 HTTP 服务器的代码。

这个方式看起来很low,但是仍然有实际价值的,就是我们将这个请求转发到对应的服务上,例如订单请求转给订单服务,评论请求转给评论服务等等。这就是后面介绍的网关要干的事情,而这里我们在Java类层面就这么做很明显就不好了。

那该怎么解决这个问题呢?我们知道,面向接口编程是解决耦合问题的法宝,于是有一伙人就定义了一个接口,各种业务类都必须实现这个接口,这个接口就叫 Servlet 接口,有时我们也把实现了 Servlet 接口的业务类叫作 Servlet。

但是这里还有一个问题,对于特定的请求,HTTP 服务器如何知道由哪个 Servlet 来处理呢?Servlet 又是由谁来实例化呢?显然 HTTP 服务器不适合做这个工作,否则又和业务类耦合了。

于是,还是那伙人又发明了 Servlet 容器,Servlet 容器用来加载和管理业务类。HTTP 服务器不直接跟业务类打交道,而是把请求交给 Servlet 容器去处理,Servlet 容器会将请求转发到具体的 Servlet,如果这个 Servlet 还没创建,就加载并实例化这个 Servlet,然后调用这个 Servlet 的接口方法。因此 Servlet 接口其实是Servlet 容器跟具体业务类之间的接口。下面我们通过一张图来加深理解。

图的左边表示 HTTP 服务器直接调用具体业务类,它们是紧耦合的。再看图的右边,HTTP 服务器不直接调用业务类,而是把请求交给容器来处理,容器通过 Servlet 接口调用业务类。因此 Servlet 接口和 Servlet 容器的出现,达到了 HTTP 服务器与业务类解耦的目的。

上面的过程,我们可以结合实际案例想象一下,假如你带着孩子去某个机构参加培训。网关就是前台, 会根据你的情况来安排到小学部还是中学部培训、艺术类还是学科类培训。而班主任就像Servlet容器,专门负责安排不同班级的事务,例如如何安排到小学数学班上课等等,但是并不负责上课。而各个学科的老师就是业务类,他不管学生如何招来了,只管如何带孩子学习。

而 Servlet 接口和 Servlet 容器这一整套规范叫作 Servlet 规范。Tomcat 按照 Servlet 规范的要求实现了 Servlet 容器,同时它们也具有 HTTP 服务器的功能。作为 Java 程序员,如果我们要实现新的业务功能,只需要实现一个 Servlet,并把它注册到 Tomcat(Servlet 容器)中,剩下的事情就由 Tomcat 帮我们处理了。

接下来我们来看看 Servlet 接口具体是怎么定义的,以及 Servlet 规范又有哪些要重点关注的地方呢?

1.Servlet 介绍

Servlet 接口定义了下面五个方法:

public interface Servlet {void init(ServletConfig config) throws ServletException;ServletConfig getServletConfig();void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;String getServletInfo();void destroy();
}

其中最重要是的 service 方法,具体业务类在这个方法里实现处理逻辑。这个方法有两个参数:ServletRequest 和 ServletResponse。ServletRequest 用来封装请求信息,ServletResponse 用来封装响应信息,因此本质上这两个类是对通信协议的封装。

比如 HTTP 协议中的请求和响应就是对应了 HttpServletRequest 和 HttpServletResponse 这两个类。你可以通过 HttpServletRequest 来获取所有请求相关的信息,包括请求路径、Cookie、HTTP 头、请求参数等。此外,我们还可以通过 HttpServletRequest 来创建和获取 Session。而 HttpServletResponse 是用来封装 HTTP 响应的。

你可以看到接口中还有两个跟生命周期有关的方法 init 和 destroy,这是一个比较贴心的设计,Servlet 容器在加载 Servlet 类的时候会调用 init 方法,在卸载的时候会调用 destroy 方法。我们可能会在 init 方法里初始化一些资源,并在 destroy 方法里释放这些资源,比如 Spring MVC 中的 DispatcherServlet,就是在 init 方法里创建了自己的 Spring 容器。

你还会注意到 ServletConfig 这个类,ServletConfig 的作用就是封装 Servlet 的初始化参数。你可以在 web.xml 给 Servlet 配置参数,并在程序里通过 getServletConfig 方法拿到这些参数。

我们知道,有接口一般就有抽象类,抽象类用来实现接口和封装通用的逻辑,因此 Servlet 规范提供了 GenericServlet 抽象类,我们可以通过扩展它来实现 Servlet。虽然 Servlet 规范并不在乎通信协议是什么,但是大多数的 Servlet 都是在 HTTP 环境中处理的,因此 Servet 规范还提供了 HttpServlet 来继承 GenericServlet,并且加入了 HTTP 特性。这样我们通过继承 HttpServlet 类来实现自己的 Servlet,只需要重写两个方法:doGet 和 doPost。

2.Servlet 容器工作过程

我在前面提到,为了解耦,HTTP 服务器不直接调用 Servlet,而是把请求交给 Servlet 容器来处理,那 Servlet 容器又是怎么工作的呢?接下来我会介绍 Servlet 容器大体的工作流程,一起来聊聊我们非常关心的两个话题:Web 应用的目录格式是什么样的,以及我该怎样扩展和定制化 Servlet 容器的功能

工作流程

当客户请求某个资源时,HTTP 服务器会用一个 ServletRequest 对象把客户的请求信息封装起来,然后调用 Servlet 容器的 service 方法,Servlet 容器拿到请求后,根据请求的 URL 和 Servlet 的映射关系,找到相应的 Servlet,如果 Servlet 还没有被加载,就用反射机制创建这个 Servlet,并调用 Servlet 的 init 方法来完成初始化,接着调用 Servlet 的 service 方法来处理请求,把 ServletResponse 对象返回给 HTTP 服务器,HTTP 服务器会把响应发送给客户端。同样我通过一张图来帮助你理解。

Web 应用

Servlet 容器会实例化和调用 Servlet,那 Servlet 是怎么注册到 Servlet 容器中的呢?一般来说,我们是以 Web 应用程序的方式来部署 Servlet 的,而根据 Servlet 规范,Web 应用程序有一定的目录结构,在这个目录下分别放置了 Servlet 的类文件、配置文件以及静态资源,Servlet 容器通过读取配置文件,就能找到并加载 Servlet。Web 应用的目录结构大概是下面这样的:

| -  MyWebApp| -  WEB-INF/web.xml        -- 配置文件,用来配置 Servlet 等| -  WEB-INF/lib/           -- 存放 Web 应用所需各种 JAR 包| -  WEB-INF/classes/       -- 存放你的应用类,比如 Servlet 类| -  META-INF/              -- 目录存放工程的一些信息

Servlet 规范里定义了ServletContext这个接口来对应一个 Web 应用。Web 应用部署好后,Servlet 容器在启动时会加载 Web 应用,并为每个 Web 应用创建唯一的 ServletContext 对象。你可以把 ServletContext 看成是一个全局对象,一个 Web 应用可能有多个 Servlet,这些 Servlet 可以通过全局的 ServletContext 来共享数据,这些数据包括 Web 应用的初始化参数、Web 应用目录下的文件资源等。由于 ServletContext 持有所有 Servlet 实例,你还可以通过它来实现 Servlet 请求的转发。

3.Servlet的扩展

不知道你有没有发现,引入了 Servlet 规范后,你不需要关心 Socket 网络通信、不需要关心 HTTP 协议,也不需要关心你的业务类是如何被实例化和调用的,因为这些都被 Servlet 规范标准化了,你只要关心怎么实现的你的业务逻辑。这对于程序员来说是件好事,但也有不方便的一面。所谓规范就是说大家都要遵守,就会千篇一律,但是如果这个规范不能满足你的业务的个性化需求,就有问题了,因此设计一个规范或者一个中间件,要充分考虑到可扩展性。Servlet 规范提供了两种扩展机制:FilterListener

Filter是过滤器,这个接口允许你对请求和响应做一些统一的定制化处理,比如你可以根据请求的频率来限制访问,或者根据国家地区的不同来修改响应内容。过滤器的工作原理是这样的:Web 应用部署完成后,Servlet 容器需要实例化 Filter 并把 Filter 链接成一个 FilterChain。当请求进来时,获取第一个 Filter 并调用 doFilter 方法,doFilter 方法负责调用这个 FilterChain 中的下一个 Filter。

Listener是监听器,这是另一种扩展机制。当 Web 应用在 Servlet 容器中运行时,Servlet 容器内部会不断的发生各种事件,如 Web 应用的启动和停止、用户请求到达等。 Servlet 容器提供了一些默认的监听器来监听这些事件,当事件发生时,Servlet 容器会负责调用监听器的方法。当然,你可以定义自己的监听器去监听你感兴趣的事件,将监听器配置在 web.xml 中。比如 Spring 就实现了自己的监听器,来监听 ServletContext 的启动事件,目的是当 Servlet 容器启动时,创建并初始化全局的 Spring 容器。

对Servlet,我们到这里就有一个大致的理解了,之后就是 Servlet 容器的具体实现。后面我们继续看Tomcat是如何设计和实现 Servlet 容器的。

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

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

相关文章

微信网页授权之使用完整服务解决方案

目录 微信网页授权能力调整造成的问题 能力调整的内容和理由 原有运行方案 is_snapshotuser字段 改造原有方案 如何复现测试场景 小结 微信网页授权能力调整造成的问题 依附于第三方的开发,做为开发者经常会遇到第三方进行规范和开发的调整,如开…

【EI会议征稿通知】2024年材料物理与复合材料国际学术会议

2024年材料物理与复合材料国际学术会议 2024 International Conference on Materials Physics and Composites(ICMPC 2024) 2024年材料物理与复合材料国际学术会(ICMPC 2024)将于2024年5月24-26日在中国成都举行。ICMPC 2024将吸引顶尖的研究人员和从业…

Python flask 模板详解

文章目录 1 概述1.1 模板简介1.2 templates 文件1.3 简单应用 2 模板语法2.1 for 循环2.2 if 判断 3 模板的继承3.1 格式要求3.2 实现示例3.3 复用父模板的内容:super 1 概述 1.1 模板简介 定义:定义好的 html 文件,用于快速开发 web 页面J…

android tv开发-1,leanback 2

目录 presenter太多,如何理清关系 动画与点击 tv的登录与设置 搜索功能 带二级菜单的页面 presenter太多,如何理清关系 leanback里面已经定义好了adapter与presenter,直接继承它就可以了 private DefaultObjectAdapter mVideoAdapter; private VideoCardPresenter mCardP…

【不单调的代码】还在嫌弃Ubuntu终端?快来试试做些Ubuntu终端的花式玩法。

🎊专栏【不单调的代码】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【Love Story】 🥰大一同学小吉,欢迎并且感谢大家指出我的问题🥰 注意: 本文是在Ubuntu环境下进…

Unity中blendtree和state间的过渡

混合树状态之间的过渡 如果属于此过渡的当前状态或下一状态是混合树状态,则混合树参数将出现在 Inspector 中。通过调整这些值可预览在混合树值设置为不同配置时的过渡表现情况。 如果混合树包含不同长度的剪辑,您应该测试在显示短剪辑和长剪辑时的过渡表…

明道云入选亿欧智库《AIGC入局与低代码产品市场的发展研究》

2023年12月27日,亿欧智库正式发布**《AIGC入局与低代码产品市场的发展研究》**。该报告剖析了低代码/零代码市场的现状和发展趋势,深入探讨了大模型技术对此领域的影响和发展洞察。其中,亿欧智库将明道云作为标杆产品进行了研究和分析。 明…

指针的学习2

目录 数组名的理解 使用指针访问数组 一维数组传参的本质 冒泡排序 二级指针 指针数组 指针数组模拟二维数组 数组名的理解 数组名是数组首元素的地址 例外: sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的…

Day11代码随想录

Day11 20. 有效的括号 力扣题目链接(opens new window) 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右…

mac如何实现升级node版本、切换node版本

一、 查看node所有版本(前提:安装了nodejs) npm view node versions二、安装指定node版本 sudo n 版本号三、检查目前安装了哪些版本的node,会出现已安装的node版本 n四、切换已安装的node版本 sudo n 版本号其他命令 1、sudo npm cache…

PyTorch 2.2 中文官方教程(十六)

介绍 torch.compile 原文:pytorch.org/tutorials/intermediate/torch_compile_tutorial.html 译者:飞龙 协议:CC BY-NC-SA 4.0 注意 点击这里下载完整的示例代码 作者: William Wen torch.compile是加速 PyTorch 代码的最新方法&#xff0…

Git快速入门+常用指令+提交规范

目录 Git创建本地仓库 IDEA集成Git Git和IDEA连接使用2 忽略文件 本地仓库常用命令 远程仓库常用命令 分支常用命令 标签操作 提交规范 Git创建本地仓库 1、创建一个文件夹,右键选择Git Bash Here 2、选择下列其中一个方法 方法一:创建初始化…

结构化/非结构化数据的介绍常用的结构化对象存储服务

一. MinIO的简介: 1.1 Minlo 介绍: Minlo 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等…

详解为什么现在的 LLMs 大都是 Decoder-only 的架构

首先概述几种主要的架构: Encoder-only:以谷歌的 BERT 为代表。 Encoder-Decoder:以谷歌的 T5、Meta 的 BART 为代表。 基于自回归空白填充的通用语言模型:清华大学的 GLM。 XLNet:XLNet 在那时是一种通用的自回归预训练方法。通过最大化所有可能的因式分解排列的对数似然…

鸡数题! - 组合数学 + 第二类斯特林数

题面 分析 第二类斯特林数 将每一位1看作球&#xff0c;元素看作盒子&#xff0c;直接计算。 代码 #include <bits/stdc.h>using namespace std; using ll long long;const int N 1e5 10; const int mod 1e9 7;int fact[N], infact[N];int qmi(int a, int b, in…

一篇文章了解区分指针数组,数组指针,函数指针,链表。

最近在学习指针&#xff0c;发现指针有这许多的知识&#xff0c;其中的奥妙还很多&#xff0c;需要学习的也很多&#xff0c;今天那我就将标题中的有关指针知识&#xff0c;即指针数组&#xff0c;数组指针&#xff0c;函数指针&#xff0c;给捋清楚这些知识点&#xff0c;区分…

深度解析Go字符串

Go语言中的字符串是一种不可变的字节序列&#xff0c;它在编程中扮演着重要的角色。接下来将深入探讨Go字符串的基本概念、常见操作、性能优化&#xff0c;以及最佳实践&#xff0c;旨在帮助大家更好地理解和利用Go语言中的字符串。 1. 字符串的基本概念 1.1 字符串的表示 在…

react native错误记录

第一次运行到安卓失败 Could not find implementation class com.facebook.react.ReactRootProjectPlugin for plugin com.facebook.react.rootproject specified in jar:file:/D:/Android_Studio_Data/.gradle/caches/jars-9/o_3a1fd35320f05989063e7069031b710f/react-nativ…

[碎碎念]全篇都是碎碎念,就不要点进来了~

去年九月份入职了新公司,本准备大干一场,结果没想到十一月公司被收购,随之而来的就是裁员 被裁之后索性就不打算找工作了,给自己一年的时间,去做点自己喜欢的事情 至于做什么还没想好,至于一年之后能不能做出来什么也不知道,一切都是不确定,一切都是未知 不过也正是如此,生活才…

综合布线技术(复习)

一、 我国在20世纪80年代末期开始引入综合布线系统&#xff0c;90年代中后期得到迅速发展 综合布线系统是建筑群或建筑物内的传输网络系统&#xff0c;他能使语音和数据通信设备、交换设备和其他信息管理系统彼此相连接 综合布线系统国家标准主要有&#xff1a;GB 5031-2016…