Servlet 生命周期详解
Servlet 的生命周期是指从 Servlet 对象被创建到被销毁的整个过程,包括其创建时机、销毁时机以及实例数量等。
Servlet 对象的管理
Servlet 对象的创建、方法调用以及销毁等一系列操作,通常不由 Java Web 开发者直接干预,而是由 Web 服务器(如 Tomcat)全权负责。Web 服务器中的这部分组件被称为 Web 容器。
手动实例化 Servlet 的影响
开发者手动使用 new 关键字创建的 Servlet 对象,并不受 Web 容器的管理。只有由 Web 容器创建并维护的 Servlet 对象,才会被纳入其管理范畴。Web 容器内部通常维护着一个集合(类似 HashMap),用于存储 Servlet 对象及其与请求路径的映射关系。
Servlet 对象的预加载
默认情况下,Web 服务器启动时并不会立即实例化所有的 Servlet 对象。这是一种合理的资源优化策略,避免在用户尚未发送请求时就消耗不必要的内存。如果在用户未访问之前就创建了大量 Servlet 对象,而这些对象又一直未被使用,则会造成资源浪费。
配置 Servlet 预加载
可以通过在 web.xml 配置文件中使用 <load-on-startup> 标签来配置 Servlet 在服务器启动时即被创建。该标签可以接受一个整数值,数值越小,优先级越高,表示越先被加载。
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
Servlet 生命周期详解
当服务器启动时,如果未配置 <load-on-startup>,Servlet 对象(如 MyServlet)的构造方法默认不会执行。
首次请求处理:
- 用户发送第一次请求时,Web 容器会执行以下操作:
- 实例化 Servlet 对象(调用无参构造方法)。
- 调用 Servlet 对象的
init()方法。 - 调用 Servlet 对象的
service()方法来处理请求。
此时的控制台输出可能如下:
MyServlet constructor executed.
MyServlet init method executed.
MyServlet service method executed.
后续请求处理:
- 用户发送后续请求时,Web 容器会重用已创建的 Servlet 实例,并直接调用其
service()方法。
此时的控制台输出可能如下:
MyServlet service method executed.
关键结论:
- 单实例性: Servlet 对象是单实例的。虽然 Servlet 类本身不符合严格的单例模式(构造方法非私有),但由于其创建由 Web 容器控制,且容器通常只创建一个实例,因此表现出单例的特性。
- 方法调用频率:
- 无参构造方法:仅在 Servlet 对象首次创建时执行一次。
init()方法:仅在 Servlet 对象首次创建后被 Web 容器调用一次。service()方法:每次接收到客户端请求时,都会被 Web 容器调用一次。
服务器关闭:
- 当服务器正常关闭时,Web 容器会调用 Servlet 对象的
destroy()方法。 destroy()方法的执行发生在 Servlet 对象内存被释放之前,用于执行一些清理操作。
此时的控制台输出可能如下:
MyServlet destroy method executed.
Servlet 方法类比人生:
- 构造方法:出生。
init()方法:接受教育/准备。service()方法:工作/服务。destroy()方法:临终/善后。
关于自定义构造方法的注意事项
如果在 Servlet 类中定义了带参数的构造方法,但没有显式定义无参构造方法,Web 容器将无法通过默认方式实例化 Servlet 对象,这通常会导致 HTTP 500(Internal Server Error)错误。因此,在 Servlet 开发中,不建议自定义构造方法,以避免潜在的实例化问题。
init() 方法的必要性
虽然 init() 方法和无参构造方法都在对象首次创建时执行一次,但 init() 方法具有其存在的必要性。Servlet 规范推荐开发者不手动定义构造方法,以确保 Servlet 可被正确实例化。init() 方法提供了一个标准化的入口点,用于执行一次性的初始化操作,例如初始化数据库连接池或线程池。
常用方法分析
service()方法: 是最常用的方法,必须实现,因为它是处理客户端请求的核心逻辑所在。init()方法: 使用频率较低,通常用于执行一次性的资源初始化,如连接池的配置。destroy()方法: 使用频率也很低,主要用于在 Servlet 对象被销毁前执行资源释放操作,如关闭数据库连接等。