Servlet的生命周期

来源:互联网 发布:复杂网络国内大牛 编辑:程序博客网 时间:2024/04/20 09:15

Servlet的生命周期

  当 servlet 第一次被创建时,init 方法会被调用。所以你的一次性设置工作的代码应该存在于 init 方法中。用户的请求会导致创建新的线程来调用之前创建的 servlet 实例的 service 方法。并发的多个请求会创建调用service方法的多个线程。同时,你也可以让你的servlet继承一个特殊的接口来保证在任何时间只能有一个线程在运行。Service方法会根据收到的HTTP请求的不同来调用不同的doXxx方法(doGet、doPost等)。最终,服务器会调用destroy方法来卸载servlet。

init方法:
  init 方法只在 servlet 首次被创建时被调用。收到用户请求时并不会调用 init 方法。所以,它常被用来作为一次性初始化的工作。Servlet 会在用户首次调用一个 servlet 的 URL 或是服务器启动之时被创建,这取决于你是否在 Web 服务器中注册了 servlet。当收到第一个用户对于这个 servlet 的请求且这个 servlet 只是正确存在于服务器的特定路径而并未明确注册时,这个 servlet 会被创建。

  init 方法有两个版本:一个没有参数;一个以 ServletConfig 对象作为参数。在 servlet 初始化时不需要任何服务器的设置信息时使用无参数的 init 方法。无参数的 init 方法定义如下:

public void init() throws ServletException {
 // 初始化代码…
}

  当 servlet 需要获得服务器的设置信息才能完成初始化时就要使用 init 的第二个版本。举例来说,servlet 可能需要有关数据库设置、password 文件、服务器特定性能参数点击计数文件、之前请求包含的 serialized cookie 数据等等的信息。这个版本的 init 方法定义如下:

public void init(ServletConfig config) throw ServletException {
 super.init(config);
 //初始化代码…..
}

  这段代码有两个地方需要注意:
  第一,这里的 init 方法有一个 ServletConfig 作为参数。ServletConfig 类提供一个 getInitParameter 方法以便你可以与 servlet 关联的初始化参数。输入(参数名)和输出(参数值)都是 String 类型。需要注意的是初始化参数的设置方法因服务器不同而略有不同。比方我们所熟悉的 Tomcat 中,servlet 的各种属性存放于 web.xml 中;据说WebLogic 应用服务器中我们使用 weblogic.properties 文件来做相同的事情.

  第二个要注意的地方方法体的第一行调用了 super.init,这非常重要!ServletConfig 对象会在servlet的其他地方用到,超类的 init 方法会将 ServletConfig 注册以便 servlet 之后用到。如果你忘了调用 super.init 将会是一件非常头疼的事情。

  核心提示:如果你使用以 ServletConfig 为参数的 init 方法,那么一定要在方法体第一行调用 super.init()。

service方法:
  当服务器接收到对 servlet 的请求时,服务器会产生一个新的线程调用 service 方法。Service 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等)然后相应的调用 doGet、doPost、doPut、doDelete 等方法。如果你的 servlet 处理 POST 请求和 GET 请求方式相同,你也可以尝试覆盖service 方法或者同时实现 doGet 和 doPost 方法。
public void service(HttpServletRequest request,
   HttpServletResponse response) throws
   ServletException, IOException {
 // Servlet 代码
}
这样做并不理想,更好的做法在 doPost 方法中调用 doGet(或者反过来),如下所示。

public void doGet(HttpServletRequest request,
   HttpServletResponse response)
   throws ServletException, IOException {
  // Servlet 代码
}
public void doPost(HttpServletRequest request,
   HttpServletResponse response)
   throws ServletException, IOException {
 doGet(request, response);
}

虽然第二种方法代码稍多了一些,当相比直接覆盖 service 方法它有五个优点:
1. 这样做确保你以后可以在子类中添加对其他请求服务的支持,如 doPut、doTrace 等。如果你你直接覆盖了 service 方法则就没有了这种可能性。
2. 你可以通过实现 getLastModified 方法来增加对修改日期的支持。如果你调用了 doGet 方法,标准 service 方法会用 getLastModified 方法设置 header 的最后修改日期然后对 GET 请求作出响应的回应(包括以修改过的 header,If-Modified-Since header)。
3. 你会自动获得对 HEAD 请求的支持。系统将只返回 doGet 方法设定的 header 和 statu code,而忽略页面体。对于定制 HTTP 客户来说 HEAD 是个很有用的请求方法。例如检查一个页面中的死链时通常使用 HEAD 替代 GET 以减轻服务器负担。
4. 你会自动获得对 OPTIONS 请求的支持。如果 doGet 方法退出,标准 service 方法通过返回一个Allow header 来询问 OPTION。Allow header 指示对GET、HEAD、OPTION和TRACE的支持。
5. 你会自动获得对TRACE请求的支持。TRACE请求方法用于客户端的调试。它只返回HTTP请求header给客户端。


  核心提示:如果你的servlet处理GET和POST方式相同,那么让你的doPost方法调用doGet方法,或者反过来。不要直接覆盖service方法。

doGET、doPost及其它doXxx方法这些方法是你servlet的主体。你百分之九十的时间都只用去关心GET和POST请求,所以你需要覆盖doGet和doPost方法。如果你需要,你也可以为了处理DELETE请求而覆盖doDelete方法;为处理PUT请求而覆盖doPut;为处理OPTIONS请求覆盖doOptions;为处理TRACE请求覆盖doTrace。

SingleThreadModel接口:
  一般情况下,系统为每个servlet创建一个实例,为每个请求创建一个新的线程。当一个新的请求到来而另一个请求产生的线程还在执行中时,就会有多个线程并发执行。这就意味着你的doGet和doPost方法必须注意到共享数据和领域的同步访问问题,因为多个线程可能会同时尝试访问同一块数据。如果你想避免多线程的并发访问,你可以使你的servlet实现SingleThreadModel接口,如下所示:

public class YourServlet extends HttpServlet
  implements SingleThreadModel {
  ...
}

如果你实现了这个接口,系统将会确保servlet的一个实例同时只会被一个请求线程访问。这通过两种方法实现。其一,系统将请求放入队列中,在同一时间只传递一个给servlet的单个实例;其二,创建一个多实例池,每个实例同一时间只控制一个请求。这意味着你不必担心servlet的实例变量的并发访问。但是,你仍然需要同步化类变量(静态变量)和servlet之外的共享数据的访问。

如果你的servlet的访问率很高,那么通同步化访问你的整个servlet将会极大降低性能。所以在使用SingleThreadModel方法时一定要考虑再三。

destroy方法:

如果服务器管理员明确要求卸载一个servelt的实例,或者某servlet已经空闲了很长时间,服务器会移除先前装载的servlet实例。移除之前服务器会调用servlet的destroy方法。Servlet可以使用这个方法关闭数据库连接、中断后台线程、向磁盘写入cookie列表、保存点击数数据以及其他清理动作。你应该意识到Web服务器会有崩溃的危险。并不是所有服务器都是用象JAVA这样相对可靠的语言编写的。某些语言容易产生譬如数组越界、违法的类型转换、指针问题造成的内存泄露等等问题。但JAVA也不是万能的,比方说它不能阻止你快步奔向电脑时被电线拌倒。同样,不要把destroy当作唯一写数据回硬盘的方法。类似保存点击数或者写cookie列表这样的行为应该有计划地周期性地进行。