Servlet的工作
Servlet 是实现javax.servlet.Servlet 的类的实例。然而,大多数 Servlet 都扩展为该接口的标准实现之一,即javax.servlet.GenericServlet和javax.servlet.http.HttpServlet。开发人员在编写 Servlets 代码时将此作为蓝图。实现 Servlet 的接口或扩展 GenericServlet/HttpServlet 类提供了创建 Servlet 的框架以及重要的默认功能。当实现任何自定义功能时,Servlet 的代码规范必须覆盖至少一种方法。
为了初始化 Servlet,Web 服务器:
- 加载 Servlet 类 [以及可能由 Servlet 引用的其他类] 并通过调用无参数构造函数创建实例
- 调用 Servlet 的init(ServletConfig config) 。 init() 仅在 Servlet 第一次加载到内存中并存储在 ServletConfig 对象中时调用一次。 Servlet 创建者可以封装在执行 Servlet 的主要代码规范之前必须运行的任何代码,例如连接到数据库。 ServletConfig 对象为 Servlet 提供有关其初始化参数的信息。这些参数提供给 Servlet 本身,并且不与任何单个请求相关联。可以通过调用 Servlet 的 getServletConfig() 来检索 ServletConfig 对象,这由 GenericServlet 处理
任何支持 Java 的 Web 服务器都会自动调用 Servlet 的 service() 以响应客户端请求。这意味着当一个 Servlet 被初始化时,它的服务( ServletRequest 请求, ServletResponse 响应)会为每个对 Servlet 的请求调用。因此,必须重写 service() 以在调用 Servlet 时提供自定义功能。但是,如果 Servlet 开发人员选择不覆盖 service(),则可以调用其他方法来响应客户端对其的请求。当需要卸载 Servlet 时,例如,因为要加载新版本或服务器正在关闭,会调用 destroy()。基于这个简短的说明,一个通用的 Servlet 的骨架已经构建完成。为简单起见,下面的 Servlet 框架不包括传递给其方法的参数或任何抛出异常的异常处理代码:
Java
pubic dass SkeletonServet extends HttpServiet
{
public void init()
{
// Initialization code goes here
}
pubicvoid service()
{
// Meaningful work happens here
}
public void destroy()
{
// Free resources here
}
}
每个 Servlet 引擎都必须遵守 Servlet 生命周期。
初始化
为了初始化 Servlet,Servlet 引擎首先定位它的类。 Servlet 类可以驻留在本地文件系统、远程文件系统或其他网络资源上。 Servlet 的引擎使用一个常用的Java类来加载工具,以将 Servlet 类加载到 JVM 中。加载后,Servlet 引擎会实例化该 servlet 类 [以及 Servlet 引用的其他类] 的实例。之后,它将调用 Servlet init(ServletConfig config)。在服务器构造 Servlet 的实例之后立即调用 init()。因此,根据服务器及其配置,可以在以下任何一种情况下调用它:
- 服务器启动时
- 当第一次请求 Servlet 时,就在调用 service() 之前
- 应服务器管理员的要求
init() 方法
init() 声明如下:
public void init(ServletConfig config) throws ServletException
在初始化期间,Servlet 必须访问两个对象:
- ServletConfig
- Servlet上下文
这是因为将 ServletConfig 的对象作为参数传递给 init()。首次设置 Servlet 时,配置数据(例如有关参数的信息和对 ServletContext 的引用)存储在 ServletConfig 对象中。以下可以通过重写 Servlet 接口的 init() 来实现的常见任务:
- 使用 ServletConfig 对象读取初始化参数
- 从持久资源中读取配置数据,如配置文件
- 初始化数据库驱动程序时,然后是连接池或日志服务
- 打开 JDBC 连接
- 将日志信息写入网络资源
无法完成其初始化过程的 Servlet 会抛出 UnavailableException,因此,只有在完成初始化之后,服务器才能准备好处理客户端请求(即在调用 service() 之前)或 Servlet 被销毁。
运行
在服务器加载并初始化一个 Servlet 之后,这个 Servlet 就可以处理客户端的请求了。它将它们处理成 service()。每个客户端的服务请求)在 Servlet 线程中单独运行。 service() 声明如下:
public void service(ServletRequest request, ServletResponse response)
方法 public void service(ServletRequest request, ServletResponse response) 抛出 ServletException 和 IOException。因为,对 Servlet 的每个请求,都会调用它的 service()。两个对象,即 ServletRequest 和 ServletResponse 作为参数传递给 service()。 ServletRequest 对象有助于访问原始请求数据,而 ServletResponse 对象提供有助于 Servlet 构建响应的方法。
Servlet 可以一次运行多个 service()。因此,重要的是 service() 是以线程安全的方式编写的。例如,如果 service() 更新了 Servlet 对象中的一些关键数据,那么对这个线程的访问应该是同步的。如果由于某种原因,服务器不应同时运行另一个 service(),则 Servlet 应实现SingleThreadModel 。这个接口保证没有两个线程会同时执行 Servlet 的 service()。
破坏
当指示卸载 Servlet 时,可能是服务器管理员或 Servlet 自身以编程方式指示卸载 Servlet 时,Servlet 引擎调用 Servlet 来销毁()。然后 Servlet 就可以进行垃圾回收了。在 init() 中分配的所有资源都应在 destroy() 中释放。 destroy() 声明如下:
public void destroy()
该方法运行一次。在重新加载并重新初始化 Servlet 之前,服务器不会再次运行它。在 destroy() 中实现的常见任务是:
- 同步清理任务,例如关闭任何打开的资源、关闭连接池
- 通知另一个应用程序 Servlet 将不再在服务中
destroy() 保证在 Servlet 的生命周期中只被调用一次。在 Servlet 的生命周期中,为销毁 Servlet 编写线程安全代码非常重要,除非 Servlet 实现SingleThreadModel ,否则为客户端请求提供服务。调用destroy() 时可能仍有线程执行service(),因此destroy() 必须是线程安全的。这些步骤说明了 Servlet 的整个生命周期,并且还以图表方式表示,如图 1 所示。