前面我们讲到CGI编程时,用户每请求一次CGI程序,服务器会创建一个单独的进程来处理请求,处理完毕后再销毁。如果并发请求数很多,CGI程序往往力不从心。而Servlet便很好解决了这个问题。服务器会在启动时或第一次请求Servlet时初始化一个Servlet对象,然后用这个Servlet对象去处理所有的客户端请求。服务器关闭才销毁这个Servlet对象。
Servlet生命周期简单的概括这就分为四步:类加载实例化--->初始化--->服务--->销毁。
Servlet生命周期是由javax.servlet.Servlet接口定义,所有的Servlet都必须实现这个接口。在Servlet接口中定义了5个方法,其中3个方法代表了Servlet的生命周期:
1、init方法:负责初始化Servlet对象。
2、service方法:负责响应客户的请求。
3、destroy方法:当Servlet对象退出生命周期时,负责释放占用的资源。
1、加载(Load)和实例化(Instantiated)
Servlet容器负责加载和实例化Servlet。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),所以我们在编写Servlet类的时候,不应该提供带参数的构造方法。Servlet容器加载Servlet,有以下几种情况:
(1)、Servlet容器启动时自动装载Servlet,读取配置文件web.XML文件中的<load-on-startup>属性,如果为1,则容器启动时加载Servlet.
(2)、在Servlet容器启动后,客户首次向Servlet发送请求。Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则加载这个Servlet。
(3)、Servlet类文件被更新后,重新加载Servlet.
2、初始化阶段:init(ServletConfig conf)
在Servlet实例化之后,Servlet容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。在初始化期间,Servlet实例可以使用Servlet容器为它准备的ServletConfig对象从Web应用程序的配置信息(在web.xml中配置)中获取初始化的参数信息。
在初始化期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常来通知容器。ServletException异常用于指明一般的初始化失败,例如没有找到初始化参数;而UnavailableException异常用于通知容器该Servlet实例不可用。例如,数据库服务器没有启动,数据库连接无法建立,Servlet就可以抛出UnavailableException异常向容器指出它暂时或永久不可用。
3、响应请求服务阶段(service)
Servlet 被初始化以后,就处于能响应请求的就绪状态。在service()方法中,Servlet实例通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。当客户端有一个请求时,Servlet容器将ServletRequest 和ServletResponse对象都转发给Servlet,这两个对象以参数的形式传给service方法。这个方法由javax.servlet.Servlet定义并由具体的Servlet 实现。
注意:客户端每次请求Servlet都会运行该方法,该方法判断访问类型,然后根据HttpServletRequest的getMethod()方法返回结果判断是执行doGet还是doPost,doPut。而且无论请求多少次Servlet,最多只有一个Servlet实例。多个客户端并发请求Servlet时,服务器会启动多个线程分别执行该Servlet的service()方法。
在service()方法执行期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常。如果UnavailableException异常指示了该实例永久不可用,Servlet容器将调用实例的destroy()方法,释放该实例。此后对该实例的任何请求,都将收到容器发送的HTTP 404(请求的资源不可用)响应。如果UnavailableException异常指示了该实例暂时不可用,那么在暂时不可用的时间段内,对该实例的任何请求,都将收到容器发送的HTTP 503(服务器暂时忙,不能处理请求)响应。
4、终止服务阶段(destroy)
当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。
小结:在整个Servlet的生命周期过程中,创建Servlet实例、调用实例的init()和destroy()方法都只进行一次,当初始化完成后,Servlet容器会将该实例保存在内存中,通过调用它的service()方法,为接收到的请求服务。Servlet有时会用到一些需要初始化与销毁的资源,因此可以把初始化资源的代码放入init()方法内,把销毁资源的代码放入destroy方法内,而不需要每次处理请求都要初始化与销毁资源。
小问:既然Servlet只有一个实例,并发访问时,服务器会派生多个线程执行Servlet的service方法,那么Servlet是否有线程的安全问题?继续学习....
----------------------------------
参考:
1、疯狂骑士的blog:http://blog.sina.com.cn/s/blog_5198c7370100cwrz.html
2、易百Servlet教程:http://www.yiibai.com/servlets/servlets_life_cycle.html