Javaweb - 10.3 Servlet 生命周期

目录

生命周期简介

生命周期测试

load-on-startup

补充:defaultServlet

Servlet 的继承结构

1. 顶级的 Servlet 接口

2. 抽线的类 GenericServlet

3. HttpServlet 抽象类

4. 自定义 Servlet

补充:

完!


生命周期简介

什么是生命周期?

        应用程序中的对象,不仅在空间上有层次结构的关系,在实践上也会因为处于运行过程中的不同阶段,而表现出不同状态和不同行为,这就是对象的生命周期

        简单的叙述生命周期:就是对象在开始创建,到最后销毁的过程。

Servlet 容器:

        Servlet 对象是 Servlet 容器创建的,生命周期方法都是由容器(我们目前使用 Tomcat)调用的。这一点和我们之前所编写的代码,由很大的不同!在今后的学习中,我们越来越多的对象,都要交给容器或框架来创建,越来越多的方法,都要由容器或框架来调用。

        我们作为程序员,要尽可能的将经历放在业务逻辑的实现上!

Servlet 主要的生命周期执行特点:

生命周期测试

还在我们前面的项目工程中,开发一个测试代码:

package com.zzz.servlet;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;/** @author zzr* @date: 2025/07/04  21:24* @description: 测试 servlet 的生命周期*/
@WebServlet("/servletLifeCycle")
public class servletLifeCycle extends HttpServlet {public servletLifeCycle() {System.out.println("构造器");}@Overridepublic void init() throws ServletException {System.out.println("初始化");}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("服务");}@Overridepublic void destroy() {System.out.println("销毁");}
}

在四处打印的地方打上断点:

debug 模式运行

为浏览器配置好 url 后,回车,发现代码卡在了“构造器”的断点处

一步一步向后运行代码

此时并没有销毁。并且,当我们在浏览器,刷新重新打开的时候,就只会再打印“服务”了~

停止项目后,会打印“销毁”

结论:

        通过生命周期的测试,我们发现:Servlet 对象在容器中是单例的。但,容器又是需要并发的,处理用户请求的,每个请求在容器中,都会开启一个线程。

        多个线程可能会使用相同的 Servlet 对象,但 Servlet 对象在容器中是单例的,所以 Servlety 的成员变量,在多个线程之中也是共享的 ==》 非常非常不建议,在 service 中修改成员变量,在并发请求的时候,会引发线程安全问题~

load-on-startup

我们在前面的 @WebServlet 提到过这个成员变量~

其值是一个数字,含义是:tomcat 在启动时,实例化该 servlet 的顺序。(如果顺序号冲突了,tomcat 会自动协调启动顺序~)

    <servlet><servlet-name>servletLifeCycle</servlet-name><servlet-class>com.zzz.servlet.servletLifeCycle</servlet-class>
<!--默认值是 -1 含义是 tomcat 启动时不会实例化该 servlet其他正整数,例如 15,含义是 在 tomcat启动时,实例化该 servlet 的顺序
--><load-on-startup>15</load-on-startup></servlet><servlet-mapping><servlet-name>servletLifeCycle</servlet-name><url-pattern>/servletLifeCycle</url-pattern></servlet-mapping>

将 servletLifeCycle 中的 @WebServlet 注释删掉,取消所有断点,直接运行程序:

发现自动就为我们准备好了构造器和初始化~

当然也可以在 @WebServlet 中设置 loadOnStartup 的值

注意:我们可以在 tomcat 文件夹下的 conf/web.xml 进行查找,发现有些序号已经占用了,1 - 5 号都已经被占用,我们如果要使用,最好不要重复 1 - 5 号~(这个序号就算不连贯也是可以的,tomcat 会自己匹配~)

补充:defaultServlet

defaulyServlet 是用于加载静态资源的 servlet,默认随服务器启动,默认启动序号为 1

Servlet 的继承结构

1. 顶级的 Servlet 接口

源码如下:

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

Servlet 规范接口,所有的 servlet 必须实现

        init:

                初始化方法,容器在构造 servlet 对象后,自动调用的方法,容器负责实例化一个 ServletConfig 对象,并在调用该方法的时候传入。

                ServletConfig 对象可以为 servlet 对象提供初始化参数

        getServletConfig:

                获取 ServletConfig 对象的方法,后续可以通过该对象获取 servlet 初始化参数

        service:

                处理请求并做出响应的服务方法,每次请求产生的时候,都是由容器调用。

                容器创建一个 ServletRequest 对象喝 ServletResponse 对象,容器在调用 service 方法时候,传入这两个对象。

        getservletInfo:

                获取 servletInfo 信息的方法

        destory:

                servlet 实例在销毁之前调用的方法,用用作资源释放

2. 抽线的类 GenericServlet

源码如下:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {private static final long serialVersionUID = 1L;private transient ServletConfig config;public GenericServlet() {}public void destroy() {}public String getInitParameter(String name) {return this.getServletConfig().getInitParameter(name);}public Enumeration<String> getInitParameterNames() {return this.getServletConfig().getInitParameterNames();}public ServletConfig getServletConfig() {return this.config;}public ServletContext getServletContext() {return this.getServletConfig().getServletContext();}public String getServletInfo() {return "";}public void init(ServletConfig config) throws ServletException {this.config = config;this.init();}public void init() throws ServletException {}public void log(String message) {ServletContext var10000 = this.getServletContext();String var10001 = this.getServletName();var10000.log(var10001 + ": " + message);}public void log(String message, Throwable t) {this.getServletContext().log(this.getServletName() + ": " + message, t);}public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;public String getServletName() {return this.config.getServletName();}
}

 GenericServlet 抽象类,是对 Servlet 接口一些固定功能的粗糙实现,以及对 service 方法的再次抽象声明,并定义了一些其他相关功能方法。

private transit ServletConfig config:

        初始化配置对象作为属性(transit 是一个特殊的关键字。当对象被序列化时,被 transit 修饰的变量不会被序列化,也就是不会被持久化存储或通过网络传输)

public GenericServle:

        构造器,为了满足继承而准备

public void destory:

        将 Servlet 中的抽象方法,重写为普通方法,在方法内部中没有任何实现的代码,称为 destory 的平庸实现 ==》 让子类可根据需要选择是否重写,实现销毁相关逻辑

public void init() {

        this.config() = config;

        this.init();

}

        tomcat 在调用 init 方法时,会读取配置信息进入一个 ServletConfig 对象,并将该对象传入 init 方法。此方法将 config 对象存储为当前的属性,并且调用了重载的无参的 init 方法

public void inti:

        重载的初始化方法,即我们重写初始化方法对应的方法。

public ServletConfig getServletConfig:

        返回 ServletConfig 的方法

public abstract void service:

        再次抽象声明 service 方法

3. HttpServlet 抽象类

在这个抽象里,侧重于 service 方法的处理

源代码较长,此处选部分重要的进行理解:

    private static final String METHOD_DELETE = "DELETE";private static final String METHOD_HEAD = "HEAD";private static final String METHOD_GET = "GET";private static final String METHOD_OPTIONS = "OPTIONS";private static final String METHOD_POST = "POST";private static final String METHOD_PUT = "PUT";private static final String METHOD_TRACE = "TRACE";

上述属性,用于定义常见请求方式名的常量值

    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest)req;response = (HttpServletResponse)res;} catch (ClassCastException var6) {throw new ServletException(lStrings.getString("http.non_http"));}this.service(request, response);}

request = (HttpServletRequest)req 和 response = (HttpServletResponse)res 都是参数的父转子操作(子类的方法属性更多一些~) 再调用重载的 service 方法

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();if (method.equals("GET")) {this.doGet(req, resp);} else if (method.equals("HEAD")) {this.doHead(req, resp);} else if (method.equals("POST")) {this.doPost(req, resp);} else if (method.equals("PUT")) {this.doPut(req, resp);} else if (method.equals("DELETE")) {this.doDelete(req, resp);} else if (method.equals("OPTIONS")) {this.doOptions(req, resp);} else if (method.equals("TRACE")) {this.doTrace(req, resp);} else {resp.sendError(501, errMsg);}}

在 protected 修饰的 service 方法中,先是获取了请求的方式,然后根据请求方式,调用对应的 do*** 方法

do*** 方法大同小异,这里以 doGet 为例:

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String msg = lStrings.getString("http.method_get_not_supported");this.sendMethodNotAllowed(req, resp, msg);}

先获取对应的字符串,然后调调用 sendMethodNotAllowed 方法,即 sendError 方法,故意响应 405 请求方式不允许的信息。

4. 自定义 Servlet

public class servlet1 extends HttpServlet {@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("servlet1 执行了");}
}

在自定义的 Servlet 中,实现接收用户请求信息,然后做出资源响应

补充:

如果我们自定义的 servlet 中,没有重写 service 方法,就会运行父类的 HttpServlet 中的 service 方法,在父类的 service 方法中,就会执行默认的 doGet 和 doPost 方法 ==》 响应 405

我们也可以自定义的 service 方法中,不重写 service 方法,直接重写 doGet 和 doPost 方法~

有些从程序员,推荐在自定义的 servlet 中重写 do*** 方法处理请求,理由:父类中的 service 方法中可能做了一些处理,如果我们直接重写 service 方法,父类中的 service 方法中的一些处理可能会失效。

但是,目前观察,直接重写 service 并不会有什么问题~

后续使用 SpringMVC 框架之后,我们则无需继承 HttpServlet,处理请求的方法也无需是 do*** 和 service 了

补充:在此处,我们自定义的 servlet 中,要么重写 service 方法,要么重写 do*** 方法~

如果同时重写了 service 和 do*** 方法,service 优先

完!

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

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

相关文章

RSA数字签名方案的C语言实现(带测试)

RSA 算法的 C语言实现通常比较复杂&#xff0c;但已经有许多密码算法库实现了 RSA 算法&#xff0c;例如OpenSSL、Libgcrypt​ 和 Botan ​等。我们可以在这些库的基础上进行配置或移植&#xff0c;从而快速实现密码算法。但这些库主要面向大量设备进行优化&#xff0c;如通用计…

创客匠人视角:知识变现与创始人 IP 打造的破局之道

当知识付费从流量红利期进入精耕细作阶段&#xff0c;为何专业能力强的内容创作者反而难以变现&#xff1f;创客匠人通过 1500 案例陪跑发现&#xff1a;缺乏 IP 思维的知识输出如同雾中航行&#xff0c;而创始人 IP 打造正是连接知识价值与商业变现的核心桥梁。一、定位重构&…

结构分析设计软件 SCIA Engineer 25.0 x64

详情 Nemetschek SCIA Engineer是一家从事多项目编程、分析和软件设计的公司。该软件具有广泛的不同功能。该软件可用于以简单的方式设计建筑物、工业工厂和桥梁。 Nemetschek SCIA Engineer软件的特点和功能&#xff1a; BIM模型人 使用网格和故事 3D风 自由负载 互联网…

怎么处理[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark

这个错误说明 Elasticsearch 的磁盘空间严重不足&#xff0c;已触及最高级别&#xff08;flood-stage&#xff09;的水位线。作为自我保护机制&#xff0c;Elasticsearch ​自动将受影响的索引设置为只读模式 (read-only-allow-delete)​&#xff0c;从而阻止写入操作&#xff…

pytorch学习-11卷积神经网络(高级篇)

2.线性模型 3.梯度下降算法 4.反向传播(用pytorch算梯度) 5.用pytorch实现线性回归 6.logistic回归 7.处理多维特征的输入 8.加载数据集 9.多分类问题 10.卷积神经网络(基础篇) 11.卷积神经网络&#xff08;高级篇&#xff09;_哔哩哔哩_bilibili 11.1 GoogleNet Google…

ubuntu 安装QT

在 Ubuntu 系统上安装 Qt 可以通过以下步骤完成&#xff0c;以下是详细的安装指南 &#xff1a; 1. 安装前的准备工作 在开始安装 Qt 之前&#xff0c;需要确保你的 Ubuntu 系统已经更新到最新版本&#xff0c;并且安装了一些必要的依赖。 1.1 更新系统 首先&#xff0c;打…

CppCon 2018 学习:RAPID PROTOTYPING OF GRAPHICS SHADERS IN

这段内容在讲**着色器&#xff08;Shader&#xff09;**的基础概念&#xff0c;尤其是它在现代 GPU&#xff08;图形处理单元&#xff09;中的作用。以下是逐条解释与理解&#xff1a; “Depicting depth perception in 3D models or illustrations by varying levels of darkn…

Angular v20版本正式发布

过去几年对 Angular 来说很具变革性,我们推出了像 Signals 这样的反应性功能和 Zoneless 应用的强大能力。我们希望这些功能可以帮助 Angular 社区构建下一代的 Web 应用,实现快速上市和强大的性能。 我们的旅程才刚刚开始!Angular v20 是最新的发布版本,我们花费了无数个小…

Oracle如何使用序列 Oracle序列使用教程

Oracle序列&#xff08;sequence&#xff09;是一种数据库项&#xff0c;能够生成一个整数序列。通常用于填充数字类型的主键列。 Oracle序列 Oracle序列使用教程&#xff1a; 1、创建序列&#xff1a; CREATE SEQUENCE sequence_name[START WITH start_num][INCREMENT BY incr…

深入探索 Vanna:让数据库交互更智能

深入探索 Vanna&#xff1a;让数据库交互更智能 在数字化时代&#xff0c;与数据库进行高效交互是许多开发者、数据分析师和企业面临的挑战。传统的 SQL 查询编写不仅需要对数据库结构有深入的了解&#xff0c;还需要花费大量的时间和精力来调试和优化。Vanna&#xff0c;一个…

C#上位机之网口通信与协议!

文章目录前言一、网口通信概念二、使用网口通信准备三、使用步骤前言 C#上位机之网口通信与协议&#xff01; 一、网口通信概念 定义 &#xff1a;Socket 可以理解为一个通信端点&#xff0c;它提供了应用程序与网络之间的接口&#xff0c;使得应用程序能够在网络上发送和接收…

Android Studio 创建类时如何自动添加类注释

打开IDEA或AS&#xff0c;点击菜单栏File——Settings——Editor——File and Code Templates。 点击右边Tab页的Includes&#xff0c;选择File Header&#xff0c;修改类头模版&#xff0c;如图&#xff1a; 记得选中Project&#xff0c;否则默认是整个AS都会进行设置

C++11:shared_ptr的设计哲学(原理+源码):内存安全和性能的架构权衡

0.简介 在C编程世界中&#xff0c;内存管理是一把双刃剑&#xff0c;手动管理带来了极致的内存控制能力&#xff0c;但也带来了像内存泄漏&#xff0c;野指针等问题&#xff1b;自动垃圾回收虽然安全&#xff0c;但却会带来一定的性能损耗。本文将介绍C11引入shared_ptr&#…

Mysql EXPLAIN 执行计划

EXPLAIN SELECT SQl。。。。界面filtered储引擎返回的数据在经过服务器层 WHERE 条件过滤后&#xff0c;剩余数据占总行数的百分比估计值rows * filtered/100 越接近100%效率越高rowspossible_keys 可能选择的索引key最终决定选择的行partitions问了哪些分区select_type查询…

力扣刷题记录【1】146.LRU缓存

前言&#xff1a; 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键字的值&…

西门子S7-1200 PLC主流通信方法及应用

一、通信基础 1. 网络术语与设备 - 关键设备&#xff1a;交换机、路由器、网关等。 - 物理接口&#xff1a;RS-485&#xff08;支持多点通信&#xff09;、RS-232C&#xff08;点对点串行通信&#xff09;。 2. OSI参考模型 - 核心框架&#xff1a;理解协议分层&…

MySQL实现任意级子目录的主要方案以及区别

常见的实现方案及区别 1. 邻接表&#xff08;Adjacency List&#xff09; 方案描述&#xff1a; 每条记录存储一个节点的父节点ID。 表结构大致&#xff1a; id INT PRIMARY KEY, name VARCHAR(...), parent_id INT -- 指向父节点的ID&#xff0c;根节点为NULL或0优点&…

Linux网络socket套接字(完)(5)

文章目录前言一、多进程版的Tcp网络程序捕捉SIGCHLD信号让孙子进程提供服务二、多线程版的Tcp网络程序三、线程池版的Tcp网络程序四、Tcp协议通讯流程通讯流程总览三次握手的过程数据传输的过程四次挥手的过程总结前言 结束喽&#xff0c;至少这个Tcp套接字有关内容要结束了~  …

Web3 Study Log 003

Web3 Study Log 003 2025-7-5 这几天各种各样的琐事&#xff0c;处理完了&#xff0c;真的烦&#xff0c;估计能消停一段时间了… 今天终于能够坐下来好好学习&#xff0c;今天学习了chainlink的使用&#xff0c;能够获取 ETH/USD 实时价格&#xff0c;然后写了一个简单的众…

Kotlin:2.1.20 的新特性

一、概述 The Kotlin 2.1.20 release is here! Here are the main highlights: Kotlin 2.1.20发布了&#xff0c;主要亮点如下&#xff1a; K2 compiler updates: updates to the new kapt and Lombok pluginsKotlin Multiplatform: new DSL to replace Gradle’s Application …