Servlet 的生命周期
Servlet 的整个生命周期由Servlet 容器管理,该容器使用javax.servlet.Servlet接口来理解和管理 Servlet 对象。所以,在创建 Servlet 对象之前,我们先来了解一下 Servlet 对象的生命周期,这其实就是了解 Servlet 容器是如何管理 Servlet 对象的。
Servlet 生命周期的阶段:Servlet 生命周期主要经历四个阶段,
- 加载一个 Servlet。
- 初始化 Servlet。
- 请求处理。
- 销毁 Servlet。
让我们详细了解每个阶段:
- 加载 Servlet :Servlet 生命周期的第一阶段涉及通过 Servlet 容器加载和初始化 Servlet。 Web 容器或 Servlet 容器可以在以下两个阶段之一加载 Servlet:
- 在使用零或正整数值配置 Servlet 时初始化上下文。
- 如果 Servlet 不在前面阶段,它可能会延迟加载过程,直到 Web 容器确定需要此 Servlet 来为请求提供服务。
Servlet 容器在这个阶段执行两个操作:
- 加载:加载 Servlet 类。
- 实例化:创建 Servlet 的实例。要创建 Servlet 的新实例,容器使用无参数构造函数。
- 初始化一个Servlet :Servlet实例化成功后,Servlet容器初始化实例化的Servlet对象。容器通过调用Servlet.init(ServletConfig)方法来初始化 Servlet 对象,该方法接受 ServletConfig 对象引用作为参数。
Servlet 容器仅在Servlet.init(ServletConfig)对象实例化成功后立即调用Servlet.init(ServletConfig)方法一次。该方法用于初始化资源,例如 JDBC 数据源。
现在,如果 Servlet 初始化失败,它会通过抛出ServletException或UnavailableException来通知 Servlet 容器。
- 处理请求:初始化后,Servlet 实例准备好为客户端请求提供服务。当 Servlet 实例被定位以服务请求时,Servlet 容器执行以下操作:
- 它创建ServletRequest和ServletResponse对象。在这种情况下,如果这是一个 HTTP 请求,那么 Web 容器会创建HttpServletRequest和HttpServletResponse对象,它们分别是ServletRequest和ServletResponse对象的子类型。
- 创建请求和响应对象后,它通过传递请求和响应对象调用 Servlet.service(ServletRequest, ServletResponse) 方法。
处理请求时的service()方法可能会抛出ServletException或UnavailableException或IOException 。
- 销毁 Servlet :当 Servlet 容器决定销毁 Servlet 时,它会执行以下操作,
- 它允许当前在 Servlet 实例的 service 方法中运行的所有线程完成它们的工作并被释放。
- 当前运行的线程完成它们的工作后,Servlet 容器调用 Servlet 实例上的destroy()方法。
destroy()方法执行后,Servlet 容器释放该 Servlet 实例的所有引用,使其符合垃圾回收条件。
Servlet 生命周期方法
Servlet 有三种生命周期方法:
- 在里面()
- 服务()
- 破坏()
让我们详细看看这些方法中的每一个:
- init() 方法:Servlet 容器调用Servlet.init()方法,表示该 Servlet 实例实例化成功,即将投入使用。
//init() method public class MyServlet implements Servlet{ public void init(ServletConfig config) throws ServletException { //initialization code } //rest of code }
- service() 方法:调用 Servlet 的service()方法来通知 Servlet 客户端请求。
- 该方法使用ServletRequest对象收集客户端请求的数据。
- 此方法使用ServletResponse对象来生成输出内容。
// service() method public class MyServlet implements Servlet{ public void service(ServletRequest res, ServletResponse res) throws ServletException, IOException { // request handling code } // rest of code }
- destroy() 方法: destroy()方法在 Servlet 的生命周期内只运行一次,并发出 Servlet 实例结束的信号。
//destroy() method public void destroy()
一旦destroy()方法被激活,Servlet 容器就会释放 Servlet 实例。
Servlet 生命周期:
Servlet 生命周期可以定义为 Servlet 从创建到销毁的各个阶段。
servlet 生命周期包括以下阶段:
- Servlet 诞生
- Servlet 已初始化
- Servlet 已准备好服务
- Servlet 正在服务
- Servlet 尚未准备好服务
- Servlet 被销毁
生命周期方法:
生命周期方法是用于控制 servlet 生命周期的方法。这些方法在 servlet 的整个生命周期中按特定顺序调用。
Servlet类提供了控制和监督 Servlet 生命周期的方法。 Servlet 接口中共有三种生命周期方法。有以下几种:
- init() 方法:
- servlet 的生命从这里开始。
- 这个方法只被调用一次来加载servlet。由于它在它的生命周期中只被调用一次,因此“连接架构”代码被写在里面,因为我们只想要一次与数据库连接。
现在出现的问题是:-
Q.为什么我们不能在构造函数中编写连接的架构代码,因为构造函数在它的整个生命中也只运行一次?
答。假设如果连接没有建立,那么我们可以从 init() 中抛出一个异常,并且其余的步骤停止执行。但是在我们不能使用的构造函数中,把它的原型扔进去,否则会出错。 - 该方法只接收一个参数,即ServletConfig对象。
- 此方法有可能抛出 ServletException。
- 一旦 servlet 被初始化,它就可以处理客户端请求了。
- init() 方法的原型:
public void init(ServletConfig con)throws ServletException{ }
其中con是 ServletConfig 对象
注意:-在 servlet 程序中,我们使用 init() 的非参数化版本。
现在,出现的问题是:-
Q. 为什么推荐使用 init() 的非参数化版本而不是上面看到的参数化版本?
答。要回答这个问题,我们必须详细说明。像开发人员一样思考,即必须有一些正当的理由,答案会让你大吃一惊。来回答:方法 1
每当 servlet 的生命周期方法开始执行时,即当 public void init(ServletConfig con) throws ServletException 被调用时,我们的类 public void init(ServletConfig con) throws ServletException 被调用,但我们必须运行初始化 servlet 配置对象的代码写在“HttpServlet”方法中 public void init(ServletConfig con) throws ServletException,即:
HttpServlet 类的编码如下:public void init(ServletConfig con) throws ServletException { //code to initialise ServletConfig object init(); //This HttpServlet has 2 init() one which is parameterized and the other one is non //parameterized.But this non parameterized version of init() has a blank body. //So this call is useless. }
现在看看我们类的编码
public void init(ServletConfig con) throws ServletException { super.init(con); //Since,our class init() will run first,but to run HttpServlet init() we // have used super keyword.And Database connectivity code will be their }
注意:-正如我们所看到的,我们必须进行总共 3 个 init() 调用。首先 init() 被我们的类调用,然后是 HttpServlet 类,然后是 HttpServlet 类的非参数化版本。
但是现在,我们将通过更少的调用来实现相同的目标:
方法 2
init() 的 HttpServlet 参数化和非参数化版本的编码将保持不变。但是在我们的类中,我们将覆盖 init() 的非参数化版本,而不是覆盖 init() 的参数化版本。让我们看看我们类的 init() 的非参数化版本的编码:
public void init() throws ServletException { //database connectivity code }
注意:由于此方法 public void init() 抛出 ServletException ,我们从 HttpServlet 类中重写,其编码如下:
public void init() throws ServletException { //empty body }
由于它的主体是空白的,因此它被称为“辅助方法” ,因为它用于覆盖目的。
现在,当 servlet 开始执行其方法时,它将调用 init() 的参数化版本。由于我们不必重写参数化版本,因此它将调用 init() 的 HttpServlet 参数化版本。由于 HttpServlet 的 init() 的参数化版本的编码与上面相同,因此,从那里它将调用 init() (即 init 的非参数化版本)。它将调用我们的类的非参数化版本的 init() 并且代码继续。
现在,如您所见,init() 调用的总数为 2,这比第一种方法少。因此,与第一种方法相比,第二种方法的执行时间更少,CPU 维护堆栈的麻烦也更少,速度也有所提高。
因此,强烈建议重写 init() 的非参数化版本。虽然两者都会运行,但由于效率优先的方法很少使用,而且在第一种方法中我们也必须使用 super 关键字。因此在下面提到的程序中,我们有覆盖 init() 的非参数化版本。
- 服务()方法:
- service() 方法是提供客户端和服务器之间连接的最重要的执行方法。
- Web 服务器调用 service() 方法来处理来自客户端(Web 浏览器)的请求并将响应发送回客户端。
- 此方法确定 Http 请求的类型(GET、POST、PUT、DELETE 等)。
- 该方法还根据需要调用doGet()、doPost()、doPut()、doDelete()等各种其他方法。
- 此方法接受两个参数。
- 该方法的原型:
public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException { }
在哪里
- req是 ServletRequest 对象,它封装了从客户端到服务器的连接
- resp是 ServletResponse 对象,它封装了从服务器到客户端的连接
- 销毁()方法:
- destroy() 方法只被调用一次。
- 它在 servlet 的生命周期结束时调用。
- 此方法执行各种任务,例如关闭与数据库的连接、释放分配给 servlet 的内存、释放分配给 servlet 的资源以及其他清理活动。
- 调用此方法时,垃圾收集器开始工作。
- 这个方法的原型是:
public void destroy() { // Finalization code...}
下面是一个示例程序来说明Java中的 Servlet:
// Java program to show servlet example
// Importing required Java libraries
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
// Extend HttpServlet class
public class AdvanceJavaConcepts extends HttpServlet
{
private String output;
// Initializing servlet
public void init() throws ServletException
{
output = "Advance Java Concepts";
}
// Requesting and printing the output
public void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException
{
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(output);
}
public void destroy()
{
System.out.println("Over");
}
}