Servlet运行原理及生命周期

news2024/11/16 11:52:20

Servlet运行原理及生命周期

  • 一、Servlet 运行原理
    • 1.1 Tomcat 的定位
    • 1.2 Tomcat 的伪代码
      • 1.2.1 Tomcat 初始化流程
      • 1.2.2 Tomcat 处理请求流程
      • 1.2.3 Servlet 的 service 方法的实现
  • 二、Servlet生命周期

一、Servlet 运行原理

承接 Servlet基础教程:https://blog.csdn.net/yyhgo_/article/details/128468738?spm=1001.2014.3001.5502

在 Servlet 的代码中我们并没有写 main 方法,那么对应的 doGet 代码是如何被调用的呢?响应又是如何返回给浏览器的?

1.1 Tomcat 的定位

我们自己的实现是在 Tomcat 基础上运行的。
在这里插入图片描述

当浏览器给服务器发送请求的时候,Tomcat 作为 HTTP 服务器,就可以接收到这个请求。

HTTP 协议作为一个应用层协议,需要底层协议栈来支持工作,如下图所示:
在这里插入图片描述

更详细的交互过程可以参考下图:
在这里插入图片描述

  1. 接收请求:
  • 用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求 。
  • 这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去。
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机 (这个过程也需要网络层和数据链路层参与)。
  • 服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成
    HTTP 请求, 并交给 Tomcat 进程进行处理(根据端口号确定进程)。
  • Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个 webapp, 再通过 Servlet Path 确定一个具体的 类。再根据当前请求的方法 (GET/POST/…), 决定调用这个类的 doGet 或者 doPost 等方法。此时我们的代码中的 doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息。
  1. 根据请求计算响应:
  • 在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码。我们自己的代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性, 例如:状态码, header, body 等。
  1. 返回响应:
  • 我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好
    的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去。
  • 此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去。
  • 这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机 (这个过程也需要网络层和数据链路层参与)。
  • 浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成
    HTTP 响应, 并交给浏览器处理。
  • 浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应。并且把 body 中的数据按照一定的格式显示在浏览器的界面上。

1.2 Tomcat 的伪代码

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

所谓 “伪代码”, 并不是一些语法严谨, 功能完备的代码, 只是通过这种形式来大概表达某种逻辑 ~~

1.2.1 Tomcat 初始化流程

class Tomcat {
    // 用来存储所有的 Servlet 对象
    private List<Servlet> instanceList = new ArrayList<>();
    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 服务器
        // 并用线程池的方式分别处理每一个 Request
        ServerSocket serverSocket = new ServerSocket(8080);
        // 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
        ExecuteService pool = Executors.newFixedThreadPool(100);

        while (true) {
            Socket socket = ServerSocket.accept();
            // 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
            pool.execute(new Runnable() {
                doHttpRequest(socket);
            });
        }
        // 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.destroy();
        }
    }

    public static void main(String[] args) {
        new Tomcat().start();
    }
}

小结:

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

1.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。

1.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);

二、Servlet生命周期

Servlet生命周期描述的是Servlet创建到销毁的过程:

  1. 当一个请求从HTTP服务器转发给Servlet容器时,容器检查对应的Servlet是否创建,没有创建就实例化该Servlet,并调用 init() 方法,init() 方法只调用一次,之后的请求都从第二步开始执行;
  2. 请求进入 service() 方法,根据请求类型转发给对应的方法处理,如doGet, doPost, 等等
  3. 容器停止前,调用 destory() 方法,进行清理操作,该方法只调用一次,随后JVM回收资源。

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

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

相关文章

spring oAuth2.0

会话 用户认证通过后&#xff0c;为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制&#xff0c;常见的有基于session方式&#xff0c;基于token方式等。 基于session的认证方式&#xff1a; 用户认证成功后&a…

Qt扫盲-QSS帮助手册使用

QSS帮助手册使用一、概述1. 查找 Qt Style Sheets Reference2. 收藏一下二、Qt Assistant 查阅帮助1. List of Stylable Widgets2. List of Properties3. List of Icons4. List of Property Types5. List of Pseudo-States6. List of Sub-Controls一、概述 导言&#xff1a;这…

基于信创运维平台,实现国产化网络自动巡检

近年来&#xff0c;以工业互联网、大数据、人工智能、5G技术等为代表的新一代信息技术飞速发展&#xff0c;推动国内企业向数字化经济的变革&#xff0c;数字化变成一股不可逆转的潮流&#xff0c;也是增强企业竞争力的关键所在。北京智和信通积极探索&#xff0c;把握新一轮数…

Pytorch模型转成onnx并可视化

文章目录转换模型前提转换方法模型可视化可能出现的报错信息ValueError: torch.nn.DataParallel is not supported by ONNX exporter, please use attribute module to unwrap model from torch.nn.DataParallel. Try torch.onnx.export(model.module, ...)RuntimeError: ONNX …

流媒体基础-RTP封装PS流

PS流格式 首条数据结构: RTP Header + PS Header +(System Header + PSM)+ PES(Header + Payload) 非首条数据结构: RTP Header + PS Header + PES(Header + Payload) 接下来记录侧重于找到 H.264 数据,所以会跳过一些暂时不关心的内容。 PS流有一个结束码 MPEG_progr…

信息学奥赛一本通 1916:【01NOIP普及组】求先序排列 | 洛谷 P1030 [NOIP2001 普及组] 求先序排列

【题目链接】 ybt 1916&#xff1a;【01NOIP普及组】求先序排列 洛谷 P1030 [NOIP2001 普及组] 求先序排列 【题目考点】 1. 二叉树 【解题思路】 已知中序、后序遍历序列&#xff0c;构建二叉树&#xff0c;而后对该二叉树做先序遍历&#xff0c;得到先序遍历序列。 该题…

质性分析软件nvivo的学习(二)

0、前言&#xff1a; 这部分内容是&#xff0c;质性分析软件nvivo的学习&#xff08;一&#xff09;的衔接内容&#xff0c;建议看完&#xff1a;质性分析软件nvivo的学习&#xff08;一&#xff09;再看这部分内容。这里的笔记都是以nvivo12作为学习案例的&#xff0c;其实不…

JVM垃圾回收的并行与并发

文章目录学习资料垃圾回收的并行与并发并发&#xff08;Concurrent&#xff09;并行&#xff08;Parallel&#xff09;并发 vs 并行总结学习资料 【尚硅谷宋红康JVM全套教程&#xff08;详解java虚拟机&#xff09;】 【阿里巴巴Java开发手册】https://www.w3cschool.cn/aliba…

RHCEansible 任务模块

文件操作模块 file模块 ansible-doc file -s path参数 &#xff1a;必须参数&#xff0c;用于指定要操作的文件或目录&#xff0c;在之前版本的ansible中&#xff0c;使用dest参数或者name参数指定要操作的文件或目录&#xff0c;为了兼容之前的版本&#xff0c;使用dest或n…

STM32的升级--ICP/ISP/IAP以及Ymodem协议分析

ICP/ISP/IAP 区别 ICP(In-Circuit Programing): 通过J-Link/SWD等下载器烧写程序&#xff0c;上位机需要借助其他硬件的参与才能更新固件&#xff0c;可以更新MCU的所有存储区域&#xff1b; ISP(In-System Programing): 通过MCU出厂时固化的一个bootloader升级程序&#xff0…

重点算法排序之快速排序、归并排序(上篇)

文章目录 一、排序的概念及常见的排序算法 二、快速排序的思想及代码详解 2、1 快速排序的思想 2、2 挖坑法 2、2、1 挖坑法实现思想 2、2、2 挖坑法举例 2、2、3 挖坑法代码实现 2、3 左右指针法 2、3、1 左右指针法实现思想 2、3、2 左右指针法举例 2、3、3 左右指针法代码…

类的成员之四:代码块

文章目录一、代码块静态代码块非静态代码块注意总结&#xff1a;由父及子&#xff0c;静态先行属性赋值总结一、代码块 1、代码块的作用&#xff1a;用来初始化类、对象 2、代码块如果有修饰的话&#xff0c;只能使用static 3、分类&#xff1a;静态代码块 vs 非静态代码块 静…

Ubuntu自动登录脚本

Ubuntu自动登录脚本一、!/usr/bin/expect -f的意义二、spawn命令行&#xff1a;三、send命令&#xff1a;四、expect五、interact命令&#xff1a;本人用xshell的SSH登录云服务器&#xff0c;需要从用户Ubuntu切换到root&#xff0c;再切换到lighthouse。编辑了一个自动切换用户…

2-1进程管理-进程与线程

文章目录1.进程2.进程控制块&#xff08;PCB&#xff09;3.进程的状态与转换4.进程通信8.线程9.线程和进程的比较10.线程的实现方式11.多线程模型20.线程的状态与转换1.进程 &#xff08;1&#xff09;进程是程序的一次执行过程 &#xff08;2&#xff09;进程是进程实体的运行…

JavaScript篇.day09-数据类型,表达式,运算符,作用域,语句,严格模式

目录1.数据类型(1)原始数据类型(2)隐式转换(3)逻辑语句中的类型转换(4)数据类型检测2.表达式3.运算符(1)分类(2)其他4.作用域5.语句6.严格模式1.数据类型(1)原始数据类型number, string, boolean, null, undefined, object object对象包含: Array, Function, Date...function f…

【微服务笔记03】微服务组件之Eureka注册中心高可用集群环境搭建

这篇文章&#xff0c;主要介绍微服务组件之Eureka注册中心高可用集群环境搭建。 目录 一、Eureka集群环境 二、搭建Eureka高可用集群环境 2.1、前提准备 &#xff08;1&#xff09;修改hosts配置文件 &#xff08;2&#xff09;创建父工程项目 2.2、搭建Eureka注册中心 …

数据可视化大屏Echarts高级开发散点图实战案例分析(地图扩展插件bmap.min.js、散点图、百度地图控件、柱图、涟漪动图、条件判断颜色)

系列文章目录 燃&#xff0c;拿来即用&#xff01;Echarts动态排名柱状图(自适应电脑和手机端)漏刻有时数据可视化Echarts组件开发(27)&#xff1a;盒须图(箱线图)前后端php交互的实战案例漏刻有时数据可视化Echarts组件开发(26):全国地图三级热力图下钻和对接api自动调用数据…

高压功率放大器在高校实验室的实际应用领域介绍

功率放大器的应用领域是很多电子工程师都关心的问题&#xff0c;那么功率放大器的使用场景又有哪些呢&#xff0c;下面来介绍一下安泰高压功率放大器在各个高校的实际应用情况。 图&#xff1a;激光切割 一、ATA-1000系列宽带功率放大器 应用领域&#xff1a;激光切割 适用高校…

Power BI 可视化修改配色

示例数据&#xff1a; 一、Power BI 按列排序 当把文本字段放在坐标轴&#xff0c;显示的顺序都乱了&#xff0c;完全不是自己想要的&#xff0c;就像下图所示&#xff1a; 默认按照Y轴的数值降序排序&#xff0c;如果选择按照month 以升序排序&#xff0c;就会如下所示&am…

【CDP】CDP集群如何通过Cloudera Manager配置使用SNMP方式转发告警

前言 这篇文章参考了大神的文章&#xff0c;如何在CDH平台上集成SNMP服务&#xff0c;然后实现了CDP集群集成SNMP服务&#xff0c;这里描述下&#xff0c;如何集成步骤&#xff0c;在CDP集群中告警是一个很重要的信息&#xff0c;最直观的衡量一个集群的健康状况&#xff0c;那…