搞定J2EE核心技术与企业应用
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.2 Servlet的生命周期

Servlet的生命周期包括加载、实例化、初始化、处理客户端请求和移除,该生命周期由javax.servlet.Servlet接口的init、service和destroy方法来实现。一个Servlet的生命周期由部署Servlet的容器来控制,其从产生到结束的流程如图5.7所示

图5.7 Servlet程序的生命周期

本书中的容器是Tomcat,当一个请求映射到一个Servlet时,该容器执行下列步骤:

01 如果一个Servlet的实例并不存在,Web容器将加载Servlet类、创建一个Servlet类的实例、调用init方法初始化Servlet实例。

02 调用service方法,传递一个请求和响应对象。

03 如果该容器要移除这个Servlet,可调用Servlet的destroy方法来结束该Servlet。

5.2.1 加载Servlet

容器负责加载和实例化一个Servlet。实例化和加载可以发生在引擎启动时,也可以推迟到容器需要该Servlet为客户请求服务时。

容器必须先定位Servlet类,在必要的情况下,容器使用通常的Java类加载工具加载该Servlet,可以从本机文件系统加载,也可以从远程文件系统甚至其他的网络服务加载。容器加载Servlet类以后,它会实例化该类的一个实例。需要注意的是,可能会实例化多个实例,例如,一个Servlet类因为有不同的初始化参数而有多个定义,或者Servlet实现SingleThreadModel而导致容器为它生成一个实例池。

5.2.2 初始化

Servlet加载并实例化后,容器必须在处理客户端请求前初始化它。初始化的过程主要是读取配置信息、数据库连接池及其他仅需要执行一次的任务。通过调用它的init方法并给它传递唯一的一个ServletConfig对象(每个Servlet定义一个)完成这个过程。给它传递的这个配置对象允许Servlet访问容器的配置信息中的名称-值对(name-value)初始化参数,这个配置对象同时给Servlet提供访问实现了ServletContext接口的具体对象的方法,该对象描述了Servlet的运行环境。

5.2.3 处理请求

在Servlet被初始化后,容器就可以使用它去处理请求了。每一个请求对象都是ServletRequest类型,同时Servlet使用ServletResponse返回该请求的处理结果,这些对象被作为service方法的参数传递给Servlet。在HTTP请求的情况下,容器必须提供代表请求和响应的HttpServletRequest及HttpServletResponse的具体实现。需要注意的是,容器可能会创建一个Servlet实例并将它放入等待服务的状态下,但是这个实例在它的生命周期中可能根本没有处理过任何请求。

容器可能同时将多个客户端请求发送给一个实例的service方法,这也就意味着开发者必须确保编写的Servlet可以处理并发问题。如果开发者想禁止这种默认的行为发生,那么他可以编写Servlet实现SingleThreadModel这个类,这样就可以保证一次只会有一个线程在执行service方法并且一次性执行完。容器可以通过将请求排队或者维护一个Servlet实例池满足这一点。如果Servlet是分布式应用的一部分,那么容器可能在该应用分布的每个JVM中都维护一个实例池。如果开发者使用synchronized关键字定义service方法(或者是doGet和doPost方法),容器将排队处理请求,这是由底层的Java运行时系统要求的。但最好不要同步service、doGet和doPost这样的服务方法。

5.2.4 服务结束

若没有要求容器将一个加载的Servlet保存多长时间,那么一个Servlet实例可能只在容器中存活了几毫秒,当然也可能是更长的任意时间。当容器决定将Servlet移除时(原因可能是保存内存资源不够或者自己被关闭),那么它必须允许Servlet释放它正在使用的任何资源并保存任何永久状态(这个过程通过调用destroy方法实现)。容器在调用destroy方法前,必须允许那些正在service方法中执行的线程执行完或者在服务器定义的时间段内执行(这个时间段在容器调用destroy之前)。一旦destroy方法被调用,容器就不会再向该实例发送任何请求了。如果容器需要再使用该Servlet,那么它必须创建新的实例。destroy方法完成后,容器必须释放Servlet实例以便它能够被回收。