Servlet 规范和 Servlet 容器

2023-10-30

如果大家觉得文章有错误内容,欢迎留言或者私信讨论~

前引

  通过之前的学习我们知道浏览器发给服务器的 HTTP 请求在服务器端需要调用服务端的程序来处理,也就是我们写的 Java 类,一般来说不同请求对应不同的 Java 类。

  那么问题来了,HTTP 请求怎么知道该调用那些类的哪些方法呢?最简单的方式就是通过 if-else 来做判断,A 请求就访问 AClass 的 A 方法, B 请求就访问 BClass 的 B 方法,但是这样业务代码和 HTTP 服务器的代码严重耦合,当我们需要更改或者新增业务方法的时候还需要去修改 HTTP 服务器的代码。

  为了解决这个问题,于是有一群人就定义了一个接口(面向接口编程是解决耦合问题的法宝),各种业务类都必须实现这个接口,这个接口就叫 Servlet 接口,有时我们也把实现了 Servlet 接口的业务类叫作 Servlet。

  但是对于特定的请求,HTTP 服务器该如何知道由哪个 servlet 来处理呢?servlet 又是由谁来进行实例化呢?显然 HTTP 服务器不适合做这个工作,不然又和业务代码耦合了。

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

在这里插入图片描述
  图的左边是 HTTP 服务器直接调用业务类,这样是紧耦合的。再看图的右边,HTTP 服务器不直接调用业务类,而是把请求交给 servlet 容器来处理,这样将原先 n 对 n 的关系变成了 1 对 1、1 对 n 的关系,以达成解耦的目的。

  而 Servlet 接口和 Servlet 容器这一整套规范叫作 Servlet 规范。Tomcat 和 Jetty 都按照 Servlet 规范的要求实现了 Servlet 容器,同时它们也具有 HTTP 服务器的功能。

  接下来让我们看看 servlet 接口具体是怎么样的。

servlet 接口

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


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 用来封装响应信息,因此本质上这两个类是对通信协议的封装。你可以通过 HttpServletRequest 来获取所有请求相关的信息,包括请求路径、Cookie、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。

servlet 容器

  之前提到,为了解耦,HTTP 服务不直接调用业务方法,而是通过 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 是如何被加载到容器中的呢?一般来说,我们是以 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 请求的转发。

扩展机制

  你发现没有,当有了这些东西之后,程序员不再需要关心 Socket 网络通信、不需要关心 HTTP 协议,也不需要关心你的业务类是如何被实例化和调用的,因为这些都被 Servlet 规范标准化了,你只要关心怎么实现的你的业务逻辑。但统一的规范容易千篇一律,但是如果这个规范不能满足你的业务的个性化需求,就有问题了,因此设计一个规范或者一个中间件,要充分考虑到可扩展性。Servlet 规范提供了两种扩展机制:Filter 和 Listener。

  • Filter

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

  • Listener

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

总结

Filter 是干预过程的,它是过程的一部分,是基于过程行为的。Listener 是基于状态的,任何行为改变同一个状态,触发的事件是一致的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Servlet 规范和 Servlet 容器 的相关文章

  • 如何在 Android 中的 Chrome 或 Firefox 等特定浏览器的 Web 视图中加载应用程序

    我是 Android 新手 我正在做一个应用程序 我需要在平板电脑上的 Web 视图中加载现有的应用程序 在平板电脑中 当我使用 Web 视图加载应用程序时 我的应用程序将加载到默认浏览器中 如何在平板电脑上的 Web 视图中的特定浏览器
  • 是否可以使用 Java 读写 Parquet,而不依赖 Hadoop 和 HDFS?

    我一直在寻找这个问题的解决方案 在我看来 如果不引入对 HDFS 和 Hadoop 的依赖 就无法在 Java 程序中嵌入读写 Parquet 格式 它是否正确 我想在 Hadoop 集群之外的客户端计算机上进行读写 我开始对 Apache
  • java 中的梵文 i18n

    我正在尝试使用来自互联网的示例 ttf 文件在 java 中使用 i18n 进行梵文 印地文 我可以加载资源包条目 还可以加载 ttf 并设置字体 但它不会根据需要呈现 jlabel 它显示块代替字符 如果我在 Eclipse 中调试 我可
  • Apache Thrift Java-Javascript 通信

    我正在编写一个基于 Apache Thrift 的 Java 服务器 它将从 Javascript 客户端接收数据 我已经完成了 Java 服务器 但问题是我可以获得 Javascript 客户端的工作示例 我无法找到一个好的示例 构建文档
  • Java Junit 测试 HTTP POST 请求

    我需要测试以下方法而不改变方法本身 该方法向服务器发出 POST 方法 但我需要制作一个独立于服务器的测试用例 在将其重定向到本地文件之前 我测试了类似的方法 但为此我将协议指定为文件 主机名指定为 localhost 端口指定为 1 我的
  • 使用 Jena 查询维基数据

    目前 Wikidata 有一个 SPARQL 端点 https query wikidata org https query wikidata org 我想使用 Jena 3 0 1 查询此网站 我使用以下代码 但收到错误消息 端点返回的
  • 绘制平滑曲线

    我想创建更平滑的曲线 而不仅仅是线角 这是我现在画的图 这是我的代码 case FREEHAND float pts float ptk ptk new float 2 imageMatrix invert inv if mCurrentS
  • 膨胀类 android.support.design.widget.NavigationView 时出错

    我按照 NavigationView 的教程进行操作 但无法解决此错误消息 Error inflating class android support design widget NavigationView 教程链接 https www
  • 在Java中如何将字节数组转换为十六进制?

    我有一个字节数组 我希望该数组的每个字节字符串转换为其相应的十六进制值 Java中有没有将字节数组转换为十六进制的函数 byte bytes 1 0 1 2 3 StringBuilder sb new StringBuilder for
  • RxJava android mvp 单元测试 NullPointerException

    我是 mvp 单元测试的新手 我想对演示者进行一个非常基本的测试 它负责登录 我只想断言 view onLoginSuccess 这是演示者代码 public LoginPresenter LoginViewContract loginVi
  • 如何允许/限制某些计算机名称访问 tomcat 服务器?

    我在我的电脑上打开了一个服务器 我所有在同一网络上的朋友都可以从他们的计算机访问它 但我只想让我的一位朋友访问 所以我尝试在我的中写下以下内容context xml
  • 获取 Future 对象的进度的能力

    参考 java util concurrent 包和 Future 接口 我注意到 除非我弄错了 只有 SwingWorker 实现类才能启动冗长的任务并能够查询进度 这就引出了以下问题 有没有办法在非 GUI 非 Swing 应用程序 映
  • Spring - 如何在不匹配列名的情况下使用 BeanPropertyRowMapper

    我正在开发一个应用程序 该应用程序已使用行映射器从纯 JDBC 转换为 Spring 模板 我遇到的问题是数据库中的列与属性名称不匹配 这阻止我使用BeanPropertyRowMapper容易地 我看到一些关于在查询中使用别名的帖子 这会
  • Proguard 正在破坏我的清洁度。 Gson 和泛型

    我有一个从持久性加载信息的函数 我只是以一种非常简单的方式告诉它的类型 该类称为SharedPreferencesHelper kt所以它是一个真正的生活问题解决者 fun
  • java.lang.UnsatisfiedLinkError - android studio gradle 中的 NDK?

    文件夹结构 app main java jni Android mk Application mk hello jni c res 在构建 gradle apply plugin com android application androi
  • Firebase:用户注册后如何进行电话号码验证?

    所以我知道我可以使用电子邮件验证或电话号码验证 但我想做的是在用户注册或登录后进行电话号码验证 如何连接这两种身份验证方法 最后 Firebase中是否有一个函数可以检查用户是否通过电话号码验证 谢谢 即使用户已通过身份验证 您仍然可以使用
  • Java 中序列化的目的是什么?

    我读过很多关于序列化的文章 以及它如何如此美好和伟大 但没有一个论点足够令人信服 我想知道是否有人能真正告诉我通过序列化一个类我们真正可以实现什么 让我们先定义序列化 然后我们才能讨论它为什么如此有用 序列化只是将现有对象转换为字节数组 该
  • com.sun.xml.ws.message.saaj.SAAJHeader 无法转换为 com.sun.xml.ws.security.opt.impl.outgoing.SecurityHeader

    我正在尝试访问第三方 Web 服务 该服务要求我创建一个传递时间信息 用户名和密码的安全标头 我在网上搜索了可行的示例 并尝试了多种方法 我正在尝试使用 Java 6 中内置的内容来做到这一点 我不确定我做错了什么 从 WSDL 生成 We
  • 我怎样才能限定我不“拥有”的自动装配设置器

    要点是 Spring Batch v2 测试框架具有JobLauncherTestUtils setJob与 Autowired注解 我们的测试套件有多个Job类提供者 由于这个类不是我可以修改的东西 我不确定如何限定它自动连接的作业 每个
  • 如何在J2ME中获取数字的幂[重复]

    这个问题在这里已经有答案了 可能的重复 J2ME power double double 数学函数实现 https stackoverflow com questions 2076913 j2me powerdouble double ma

随机推荐

  • 单片机延时程序分析

    5课 单片机延时程序分析 上一次课中 我们已经知道 程序中的符号R7 R6是代表了一个个的RAM单元 是用来放一些数据的 下面我们再来看一下其它符号的含义 DELAY MOV R7 250 6 D1 MOV R6 250 7 D2 DJNZ
  • 当前所处的经济周期位置和未来资本市场展望

    当前所处的经济周期位置和未来资本市场展望 pdf 省时查方案 专业 及时 全面的营销策划方案库 免费下载 2023年8月份全网热门报告合集 ChatGPT提词示例 让你的ChatGPT聪明100倍 超百页干货资料 AI应用的难点 痛点与未来
  • 国内镜像源(阿里、网易、清华、中科大) ubuntu20.04下载地址超快

    1下载ubuntu系统镜像的国内源地址 一个不行换一个 下载ubuntu系统镜像的国内源地址 超快 国内镜像源 阿里 网易 清华 中科大 ubuntu20 04下载地址 清华源 https mirrors tuna tsinghua edu
  • Python小游戏项目《唐僧大战白骨精》1.0版

    小游戏 唐僧大战白骨精 1 身份选择 显示提示信息 欢迎光临 xxx 游戏 请选择你的身份 1 xxx 2 xxx 请选择 x 根据用户选择来分配身份 显示不同的提示消息 1 2 3 2 游戏进行 显示玩家的基本信息 攻击力 生命值 显示玩
  • 学习SQL Server推荐的10本书

    今天在sqlmag上闲逛 看到了这篇一年前写的文章 正好在看其中的一本 推荐给大家 原文地址 Top 10 Highly Recommended SQL Server Books 第一本 SQL Server 2014介绍 微软出品 主要介
  • 与国自然焦虑对线的感悟

    一 写在前面 连续申请了三年的国自然青年基金 今年终于中了 双非高校 三无人员 投的医学基础口 但我的研究本底并不是基础研究方向 算是小跨领域了 因此前两年一直在陪跑 二 出师不利 第一年信心满满 然后被现实无情毒打 直接emo了2天 主要
  • comsol 多物理场仿真流程-以开关柜为例

    1 主要方法 在SolidWorks中对于开关柜的内部与外部结构进行建模 生成能够导入软件计算的3D模型 将SolidWorks中的模型导入comsol软件中 设置参数进行多物理场耦合仿真 1 具体步骤 删除高压开关柜中对其内部电磁环境无较
  • 如何使用Qt软件实现一个圆形按钮

    要使用Qt软件实现一个圆形按钮 可以按照以下步骤进行操作 使用以上步骤 你就可以在Qt软件中实现一个圆形按钮 记得根据自己的需要调整按钮的样式和布局 在Qt的项目中创建一个新的QPushButton控件 或者使用现有的QPushButton
  • JAVA实现CSV文件转List

    首先来说说解决问题的思路 先创建一个类Test来接收csv文件中的内容 然后通过解析csv文件 讲内容转换为List
  • 今天给一个shopify网站修改了一下

    今天的客户是浙江的 他们没有设计稿 给了我一个需要模仿的网站 要求是把自己的商品详情页搞成和对标网站一样的 这个事情两天给搞定了 他们很满意 对标网站是一个卖狗粮的 他们是卖玩具的 哈哈 但是因为没有设计稿 修修补补了很多地方 还要求在页面
  • 装载问题(回溯法)

    1 具体问题 一批集装箱共n个要装上2艘载重量分别为c1和c2的轮船 其中集装箱i的重量为Wi且W1 W2 Wn lt c1 c2 试确定一个合理的装载方案使这n个集装箱装上这两艘轮船 2 问题分析 容易去证明 如果一个装载问题有解 则采用
  • 什么软件做可视化大屏最简单、最快?

    做可视化大屏的工具有很多 但要说什么软件做可视化大屏最简单 最快 那必然是BI系统 特别是国产BI系统奥威BI 奥威BI系统能提供大量的模板功能 精简操作 提高效率 而且采用的是零编程 即点击 拖拉拽这样的方式来制作报表 使用门槛低 即使是
  • ros1 bag to ros2 bag play

    有个问题是 ros1格式的bag需要转到ros2格式的bag 怎么弄 需要注意的是 这里只能提供标准信息的rosbag转换 如果带有自定义msg的rosbag 应该是不可以的 请注意一下 解决办法 安装依赖 sudo apt install
  • 【IDEA】对于IDEA出现程序包无法找到或者js代码无法加载问题的6种解决方案

    前言 在平时做项目时 我们有时候会出现各种奇奇怪怪的bug 标题所列举的这两种情况我都遇到过 搜的博客解决方案也很多都是一样的 今天我就给大家汇总一下解决方案 一 如果你是使用maven管理项目 在idea右上角能够看到Maven 点击Ma
  • java 比较两个类_Java 中有没有直接可以判断两个类是否相同的方法?

    展开全部 java没有直接判e5a48de588b662616964757a686964616f31333337623436断两个雷是否相同 但提供了如下方法 1 如果说两个对象的判断可以使用 equals 或者 2 如果判断对象是否某个类
  • JVM知识点(二)

    1 G1垃圾收集器 XX MaxGCPauseMillis 10 G1的参数 表示在任意1s时间内 停顿时间不能超过10ms G1将堆切分成很多小堆区 Region 每一个Region可以是Eden Survivor或Old区 这些区在内存
  • 三种memcached的JAVA客户端比较

    Memcached客户端程序 Memcached的java客户端已经存在三种了 官方提供的基于传统阻塞io由Greg Whalin维护的客户端 DustinSallings实现的基于java nio的Spymemcached XMemcac
  • Python3,我用这种方式讲解python模块,80岁的奶奶都说能理解。建议收藏 ~ ~

    Python模块讲解 1 引言 2 python模块详解 2 1 含义 2 2 代码示例 2 3 进阶 3 总结 1 引言 小屌丝 鱼哥 你看天上的月亮越来越圆了 小鱼 唉 又是一年团圆夜 又是一年中秋节 小屌丝 嘿嘿 可不滴 这个中秋 你
  • GIT error: object file is empty?

    how to fix GIT error object file is empty 前几天在使用git status 产生了一个错误 经过多方搜索 找到一篇文章 现简述一下大意 原文 开始 当我尝试提交一个修改时 我得到了一个错误 erro
  • Servlet 规范和 Servlet 容器

    如果大家觉得文章有错误内容 欢迎留言或者私信讨论 前引 通过之前的学习我们知道浏览器发给服务器的 HTTP 请求在服务器端需要调用服务端的程序来处理 也就是我们写的 Java 类 一般来说不同请求对应不同的 Java 类 那么问题来了 HT