📜  Servlet 的生命周期

📅  最后修改于: 2022-05-13 01:54:46.017000             🧑  作者: Mango

Servlet 的生命周期

Servlet 的整个生命周期由Servlet 容器管理,该容器使用javax.servlet.Servlet接口来理解和管理 Servlet 对象。所以,在创建 Servlet 对象之前,我们先来了解一下 Servlet 对象的生命周期,这其实就是了解 Servlet 容器是如何管理 Servlet 对象的。

Servlet 生命周期的阶段:Servlet 生命周期主要经历四个阶段,

  • 加载一个 Servlet。
  • 初始化 Servlet。
  • 请求处理。
  • 销毁 Servlet。

让我们详细了解每个阶段:

  1. 加载 Servlet :Servlet 生命周期的第一阶段涉及通过 Servlet 容器加载和初始化 Servlet。 Web 容器或 Servlet 容器可以在以下两个阶段之一加载 Servlet:
    • 在使用零或正整数值配置 Servlet 时初始化上下文。
    • 如果 Servlet 不在前面阶段,它可能会延迟加载过程,直到 Web 容器确定需要此 Servlet 来为请求提供服务。

    Servlet 容器在这个阶段执行两个操作:

    • 加载:加载 Servlet 类。
    • 实例化:创建 Servlet 的实例。要创建 Servlet 的新实例,容器使用无参数构造函数。

  2. 初始化一个Servlet :Servlet实例化成功后,Servlet容器初始化实例化的Servlet对象。容器通过调用Servlet.init(ServletConfig)方法来初始化 Servlet 对象,该方法接受 ServletConfig 对象引用作为参数。

    Servlet 容器仅在Servlet.init(ServletConfig)对象实例化成功后立即调用Servlet.init(ServletConfig)方法一次。该方法用于初始化资源,例如 JDBC 数据源。

    现在,如果 Servlet 初始化失败,它会通过抛出ServletExceptionUnavailableException来通知 Servlet 容器。

  3. 处理请求:初始化后,Servlet 实例准备好为客户端请求提供服务。当 Servlet 实例被定位以服务请求时,Servlet 容器执行以下操作:
    • 它创建ServletRequestServletResponse对象。在这种情况下,如果这是一个 HTTP 请求,那么 Web 容器会创建HttpServletRequestHttpServletResponse对象,它们分别是ServletRequestServletResponse对象的子类型。
    • 创建请求和响应对象后,它通过传递请求和响应对象调用 Servlet.service(ServletRequest, ServletResponse) 方法。

    处理请求时的service()方法可能会抛出ServletExceptionUnavailableExceptionIOException

  4. 销毁 Servlet :当 Servlet 容器决定销毁 Servlet 时,它会执行以下操作,
    • 它允许当前在 Servlet 实例的 service 方法中运行的所有线程完成它们的工作并被释放。
    • 当前运行的线程完成它们的工作后,Servlet 容器调用 Servlet 实例上的destroy()方法。

    destroy()方法执行后,Servlet 容器释放该 Servlet 实例的所有引用,使其符合垃圾回收条件。

Servlet 生命周期方法

Servlet 有三种生命周期方法:

  • 在里面()
  • 服务()
  • 破坏()

让我们详细看看这些方法中的每一个:

  1. init() 方法:Servlet 容器调用Servlet.init()方法,表示该 Servlet 实例实例化成功,即将投入使用。
    //init() method
    
    public class MyServlet implements Servlet{
       public void init(ServletConfig config) throws ServletException {
            //initialization code
       }
        //rest of code
    }
    
  2. 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
    }
    
  3. 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() 方法:
    1. servlet 的生命从这里开始。
    2. 这个方法只被调用一次来加载servlet。由于它在它的生命周期中只被调用一次,因此“连接架构”代码被写在里面,因为我们只想要一次与数据库连接。
      现在出现的问题是:-
      Q.为什么我们不能在构造函数中编写连接的架构代码,因为构造函数在它的整个生命中也只运行一次?
      答。假设如果连接没有建立,那么我们可以从 init() 中抛出一个异常,并且其余的步骤停止执行。但是在我们不能使用的构造函数中,把它的原型扔进去,否则会出错。
    3. 该方法只接收一个参数,即ServletConfig对象。
    4. 此方法有可能抛出 ServletException。
    5. 一旦 servlet 被初始化,它就可以处理客户端请求了。
    6. 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() 的非参数化版本。

  • 服务()方法:
    1. service() 方法是提供客户端和服务器之间连接的最重要的执行方法。
    2. Web 服务器调用 service() 方法来处理来自客户端(Web 浏览器)的请求并将响应发送回客户端。
    3. 此方法确定 Http 请求的类型(GET、POST、PUT、DELETE 等)。
    4. 该方法还根据需要调用doGet()、doPost()、doPut()、doDelete()等各种其他方法。
    5. 此方法接受两个参数。
    6. 该方法的原型:
      public void service(ServletRequest req, ServletResponse resp) 
      throws ServletException, IOException { }

      在哪里

      • req是 ServletRequest 对象,它封装了从客户端到服务器的连接
      • resp是 ServletResponse 对象,它封装了从服务器到客户端的连接
  • 销毁()方法:
    1. destroy() 方法只被调用一次。
    2. 它在 servlet 的生命周期结束时调用。
    3. 此方法执行各种任务,例如关闭与数据库的连接、释放分配给 servlet 的内存、释放分配给 servlet 的资源以及其他清理活动。
    4. 调用此方法时,垃圾收集器开始工作。
    5. 这个方法的原型是:
      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");
      }
}