Servlet生命周期简介:理解一个对象在程序中的存活时间,有利于提高代码质量。也是剖析每个程序运行原理的关键。
一、对象的生命周期
1. 创建阶段(Created)
被实例化(new)调用构造器时
· Jvm为对象分配存储空间
· 开始构造对象
· 从父类到子类对static成员进行初始化
· 父类成员变量按照顺序初始化,递归调用父类的构造方法
· 子类成员变量按照顺序初始化,子类构造方法调用一旦对象被创建,并有某个引用指向它,这个对象的状态就切换到了应用阶段(In Use)
2.应用阶段(In Use)
对象至少被一个强引用持有并且对象在作用域内
3. 不可见阶段(Invisible)
程序本身不再持有该对象的任何强引用,但是这些引用可能还存在着;一般具体是指程序的执行已经超过该对象的作用域了
4. 不可达阶段(Unreachable)
该对象不再被任何强引用所持有;可能仍被JVM等系统下的某些已经装载的惊天变灵或者线程或JNI所持有,这些特殊的强引用被称为GC root,这种情况容易导致内存泄露,无法被回收
5. 收集阶段(Collected)
对象不可达,并且GC已经准备好对该对象占用的内存空间重新分配的时候,处于收集阶段。如果重写了finazlie()方法,则会去执行该方法。
尽量不要重写finazlie()方法,因为有可能影响JVM的对象分配与回收速度或者可能造成该对象的再次复活
6. 终结阶段
当对象执行完finalize()方法之后,仍然处于不可达状态时,则该对象进入终结阶段。在这个阶段,内存空间等待GC进行回收
7. 对象空间的重新分配
GC对该对象占有的内存空间进行回收或者再分配,该对象彻底消失(jvm的垃圾回收机制)
二、Servlet的生命周期
Servlet运行原理
Servlet生命周期定义了一个Servlet如何被加载、初始化,以及它怎样接收请求、响应请求,提供服务。在讨论Servlet生命周期之前,先让我们来看一下这几个方法:
1. init()方法
在Servlet的生命周期中,仅执行一次init()方法,它是在服务器装入Servlet时执行的,可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init();
2. service()方法 (重点)
它是Servlet的核心,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。(也就说是所有请求的入口)
注意:服务器是默认调用service()方法的然后会根据条件,再调用对对应的do方法
如果重写service()方法则do等功能方法失效。不建议重写
3. destroy()方法
仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
其实Servlet的生命周期就是对象的生命周期,只不过有些阶段是容器决定的
主要可以总结为为三个阶段:
1,初始化阶段 调用init()方法
2,响应客户请求阶段 调用service()方法
3,终止阶段 调用destroy()方法
Servlet的生命周期是由Servlet容器来控制的,它始于装入Web服务器的内存时,并在终止或重新装入Servlet时结束。这项操作一般是动态执行的。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。
在代码中,Servlet生命周期由接口javax.servlet.Servlet定义。所有的Java Servlet必须直接或间接地实现javax.servlet.Servlet接口,这样才能在Servlet Engine上运行。javax.servlet.Servlet接口定义了一些方法,在Servlet的生命周期中,这些方法会在特定时间按照一定的顺序被调用。
Servlet生命周期
加载和实例化Servlet
我们来看一下Tomcat是如何加载的:
1. 如果已配置自动装入选项,则在启动时自动载入。
2. 在服务器启动时,客户机首次向Servlet发出请求。
3. 重新装入Servlet时。(反射)
当启动Servlet容器时,容器首先查找一个配置文件web.xml,这个文件中记录了可以提供服务的Servlet。每个Servlet被指定一个Servlet名,也就是这个Servlet实际对应的Java的完整class文件名。Servlet容器会为每个自动装入选项的Servlet创建一个实例。所以,每个Servlet类必须有一个公共的无参数的构造器。
初始化
当Servlet被实例化后,Servlet容器将调用每个Servlet的init方法来实例化每个实例,执行完init方法之后,Servlet处于“已初始化”状态。所以说,一旦Servlet被实例化,那么必将调用init方法。通过Servlet在启动后不立即初始化,而是收到请求后进行。在web.xml文件中用<load-on-statup> ...... </load-on-statup>对Servlet进行预先初始化。
初始化失败后,执行init()方法抛出ServletException异常,Servlet对象将会被垃圾回收器回收,当客户端第一次访问服务器时加载Servlet实现类,创建对象并执行初始化方法。
init()方法只被调用一次。
请求处理
Servlet 被初始化以后,就处于能响应请求的就绪状态。每个对Servlet的请求由一个Servlet Request对象代表。Servlet给客户端的响应由一个Servlet Response对象代表。对于到达客户机的请求,服务器创建特定于请求的一个“请求”对象和一个“响应”对象。调用service方法,这个方法可以调用其他方法来处理请求。
Service方法会在服务器被访问时调用,Servlet对象的生命周期中service方法可能被多次调用,由于web-server启动后,服务器中公开的部分资源将处于网络中,当网络中的不同主机(客户端)并发访问服务器中的同一资源,服务器将开设多个线程处理不同的请求,多线程同时处理同一对象时,有可能出现数据并发访问的错误。
另外注意,多线程难免同时处理同一变量时(如:对同一文件进行写操作),且有读写操作时,必须考虑是否加上同步,同步添加时,不要添加范围过大,有可能使程序变为纯粹的单线程,大大削弱了系统性能;只需要做到多个线程安全的访问相同的对象就可以了。
终止阶段/卸载Servlet
destroy()方法
当服务器不再需要Servlet实例或重新装入时,会调用destroy方法,使用这个方法,Servlet可以释放掉所有在init方法申请的资源。一个Servlet实例一旦终止,就不允许再次被调用,只能等待被卸载。
Servlet一旦终止,Servlet实例即可被垃圾回收,处于“卸载”状态,如果Servlet容器被关闭,Servlet也会被卸载,一个Servlet实例只能初始化一次,但可以创建多个相同的Servlet实例。如相同的Servlet可以在根据不同的配置参数连接不同的数据库时创建多个实例。
Java实例
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletLifeCycle extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* 在Servlet的生命周期中,仅执行一次init()方法,
* 它是在服务器装入Servlet时执行的,可以配置服务器,
* 以在启动服务器或客户机首次访问Servlet时装入Servlet。
* 无论有多少客户机访问Servlet,都不会重复执行init();
* 所以可以用来初始化一些参数
*/
public void init() throws ServletException {
System.out.println("init() 用来初始化一些参数");
}
/**
* 服务器是默认调用service()方法的然后会根据条件,
* 再调用对对应的do方法如果重写service()方法则do等功能方法失效。
* 不建议重写
*/
@Override
public void service(HttpServletRequest req, HttpServletResponse resp){
System.out.println("service() 重写之后不掉用其他doGet方法");
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("如果重写service() 则不会被调用");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
/**
* 仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。
* 属于垃圾回收机制
*/
public void destroy() {
System.out.println("destroy在服务器段停止且卸载servlet是执行该方法");
}
}