1.    JavaWeb应用的生命周期

JavaWeb应用的生命周期是由Servlet容器来控制的。归纳起来,JavaWeb应用的生命周期包括三个阶段:

  • 启动阶段:加载Web应用的有关数据,创建ServletContext对象,对Filter(过滤器)和一些Servlet进行初始化。
  • 运行时阶段:为客户端提供服务。
  • 终止阶段:释放Web应用所占用的各种资源。
1.1    启动阶段

Servlet容器在启动JavaWeb应用时,会完成以下操作:

  1. 把web.xml文件中的数据加载到内存中。
  2. 为JavaWeb应用创建一个ServletContext对象。
  3. 对所有的Filter进行初始化。Filter的作用将会在很多章之后再介绍。
  4. 对那些需要在JavaWeb应用启动时就被初始化的Servlet进行初始化。Servlet容器初始化Servlet的具体步骤参见第2.1节。
1.2    运行时阶段

这是JavaWeb应用最主要的生命阶段。在这个阶段,它的所有Servlet都处于待命状态,随时可以相应客户端的特定请求,提供相应的服务。加入客户端请求的Servlet还不存在,Servlet容器会先初始化Servlet,然后再调用它的service()方法。

1.3    终止阶段

Servlet容器在终止JavaWeb应用时会完成以下操作:

  1. 销毁JavaWeb应用中所有处于运行时状态的Servlet。
  2. 销毁JavaWeb应用中所有处于运行时状态的Filter。
  3. 销毁所有域JavaWeb应用相关的对象,如ServletContext对象等,并且释放Web应用所占用的相关资源。
1.4   用Tomcat的管理平台惯例Web应用的生命周期

当Servlet容器启动时,会启动JavaWeb应用。当JavaWeb应用启动后,就处于运行时状态。当Servlet容器被关闭时,Servlet容器会先终止所有的JavaWeb应用。由此可见,在一般情况下,Web应用随着Servlet的启动、运行、终止而启动、运行、终止。+

Tomcat作为Servlet容器的一种具体实现,还提供了一个管理平台,通过该平台,用户可以在Tomcat运行时,手工管理单个Web应用的生命周期。以下是通过该管理平台来管理单个Web应用的生命周期的步骤,其中步骤一只需在Tomcat安装后操作一次就行。

    1.修改tomcat的conf/tomcat-users.xml文件,在其中加入一个<user>元素,tomcat 9对应的内容为 :

<tomcat-users>
  <role rolename="manager-gui"/>
  <user username="tomcat" password="tomcat" roles="manager-gui"/>
</tomcat-users>

    2.启动tomcat

  3.Tomcat管理平台本身也是一个JavaWeb应用,它在webapps/manager目录下。该Web应用的主页的url为localhost:8080/manager/html,通过浏览器访问该URL,将会弹出一个身份验证对话框,在该窗口中输入用户“tomcat“和密码”tomcat“,就可以进入管理平台的主页。

Java的生命周期所经历的类 javaweb生命周期_tomcat

该页面列出了已经在Tomcat发布的所有web应用,对于每个Web应用都提供了Start、Stop、Reload、Undeploy四个操作。其中Reload操作等价于先终止当前Web应用,再重新启动当前Web应用;Undeploy操作即从Tomcat中卸除当前Web应用,Web应用的文件会被删除。

2.    Servlet的生命周期

JavaWeb应用的生命周期由Servlet容器控制,而作为JavaWeb应用最核心的组件的Servlet也不例外。Servlet的生命周期可以分为三个阶段:初始化阶段、运行时阶段和销毁阶段。在Servlet接口中定义了3个方法:init() service() destroy(),它们将分别在Servlet的不同阶段被Servlet容器调用。

2.1    初始化阶段

Servlet的初始化阶段包括4个步骤:

  1. Servlet容器加载Servlet类,把它的.class文件中的数据读取到内存中。
  2. Servlet容器创建ServletConfig对象。Servlet对象包含了特定Servlet的初始化配置信息,如Servlet的初始参数。此外,Servlet容器还会使得ServletConfig对象与当前应用的ServletContext关联。
  3. Servlet容器创建Servlet对象。
  4. Servlet容器调用Servlet对象的init(ServletConfig config)方法,建立起Servlet对象与ServletConfig对象的关联关系。

以上初始化步骤创建了Servlet对象和ServletConfig对象,并且建立了 Servlet——ServletConfig——ServletContext三者之间的关联关系。当容器初始化完Servlet后,Servlet对象只要公国getServletContext()方法就能得到当前Web应用的ServletContext对象。

在下列情况之一,Servlet会进入初始化阶段。

  • 当前Web应用处于运行时阶段,特定Servlet被客户端首次请求访问,多数Servlet都会在这种情况下被容器初始化。
  • 如果web.xml文件中为一个Servlet设置了<load-on-startup>元素,那么当容器启动Servlet所属的Web应用时,就会初始化这个Servlet。多个Servlet设置<load-on-startup>的话,由该元素的值来决定谁先启动(例如,一个1一个2,1的先启动)。
  • 当Web应用被重新启动时,Web应用中的所有Servlet都会在特定的时刻被重新初始化。
2.2    运行时阶段

与JavaWeb应用一样,运行时阶段是Servlet生命周期中最重要的阶段,在这个阶段Servlet可以随时响应客户端的请求。当Servlet容器接收到要求访问特定Servlet的客户请求时,Servlet容器会创建针对于这个请求的ServletRequest独对象和ServletResponse对象,然后调用相应Servlet对象的service()方法。service()方法从ServletRequest对象中获取客户请求信息并处理该请求,再通过ServletResponse对象生成响应结果。

当Servlet容器把Servlet生成的响应结果发送给了客户,Servlet容器就会销毁ServletRequest对象和ServletResponse对象。

2.3    销毁阶段

当web应用被终止时,Servlet容器会先调用web应用中所有Servlet对象的destroy()方法,然后再销毁这些Servlet对象。在destroy()方法的实现中,可以释放Servlet所占用的资源(例如关闭文件输入流和输出流,关闭与数据库的连接等)。

此外,容器还会销毁与Servlet对象关联的ServletConfig对象。

2.4    范例

在Servlet的生命周期中,Servlet的初始化和销毁只会发生一次,因此init()和destroy()方法只会被容器调用一次,而service()方法可能会被容器调用多次,这取决于客户端请求访问Servlet的次数。

例程中的LifeServlet类用于演示Servlet的声明周期,它定义了3个用于跟踪Servlet生命周期的实例变量:initVar,serviceVar,destroyVar,分别统计三个方法被调用的次数。

import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;

public class LifeServlet extends GenericServlet {
    private int initVar = 0;
    private int serviceVar = 0;
    private int destroyVar = 0;
    private String name;
    @Override
    public void init(ServletConfig config) throws ServletException{
        super.init(config);
        name = config.getServletName();
        initVar ++;
        System.out.println(name+">init():Servlet被初始化了"+initVar+"次");
    }

    @Override
    public void destroy() {
        destroyVar++;
        System.out.println(name+">destroy():Servlet被销毁了"+destroyVar+"次");
        super.destroy();
    }

    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException,ServletException {
        serviceVar++;
        System.out.println(name+">service():Servlet共响应了"+serviceVar+"次请求");

        String content1 = "初始化次数:"+initVar;
        String content2 = "销毁次数" + destroyVar;
        String content3 = "响应客户端请求次数" + serviceVar;

        servletResponse.setContentType("text/html;charset=GB2312");

        PrintWriter out = servletResponse.getWriter();
        out.println("<html><head><title>LifeServletPage</title></head>");
        out.println("<body>");
        out.println("<h1>"+content1+"</h1>");
        out.println("<h1>"+content2+"</h1>");
        out.println("<h1>"+content3+"</h1>");
        out.println("</body></html>");

        out.close();
    }
}

web.xml

</servlet-mapping>
    <servlet>
        <servlet-name>lifeInit</servlet-name>
        <servlet-class>LifeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>lifeInit</servlet-name>
        <url-pattern>/lifeInit</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>life</servlet-name>
        <servlet-class>LifeServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>life</servlet-name>
        <url-pattern>/life</url-pattern>
    </servlet-mapping>

以上配置代码定义了两个Servlet,lifeInit和life,它们都对应LifeServlet类。二者之间的区别在于,lifeInit被设置为在web应用启动时就需要被初始化。

配置完成后,启动我们的应用。当应用启动时就会初始化lifeInit。tomcat控制台会输出一个lifeInit的init()方法的打印结果:

Java的生命周期所经历的类 javaweb生命周期_初始化_02


再通过浏览器来访问lifeInit。localhost:8080/lifeInit,生成的HTML页面如下所示:

Java的生命周期所经历的类 javaweb生命周期_tomcat_03


刷新该页面,就会发现响应次数增加了1,而初始化次数一直是1。

然后,通过tomcat的管理平台或IDEA的控制台手工终止应用,Servlet容器此时会调用lifeInit的destroy()方法,并输出打印结果。

再次启动应用,访问localhost:8080/life。只有当客户端首次访问life时,Servlet容器才会初始化life,调用它的init方法,tomcat控制台继而才会输出life的init()方法的打印结果。

最后,打开两个浏览器,分别多次访问life和lifeInit。从它们各自生成的HTML页面可以看出,二者都拥有自己的serviceVar变量,它们会各自不断递增。由此可见,life和lifeInit各对应一个LifeServlet对象。也就是说,当容器初始化life和lifeInit时,会分别创建一个Servlet对象。