8.1 Servlet生命周期
Servlet的生命周期是指从Servlet被创建到最终销毁所经历的一系列阶段。主要包括以下四个阶段:
-
实例化:当Web容器首次接收到请求该Servlet的HTTP请求时,如果Servlet还没有实例化,容器会根据
web.xml配置或注解信息加载并实例化Servlet类。 -
初始化:在Servlet实例化后,容器调用其
init()方法进行初始化。通常情况下,开发者会重写这个方法来执行一次性设置操作,例如读取配置、初始化数据源等。可以通过实现ServletConfig接口的对象传入初始化参数。 -
服务请求:每当接收到一个与Servlet关联的HTTP请求时,Web容器都会为请求分配一个新的线程,并调用Servlet的
service()方法。service()方法会根据请求类型(GET、POST等)调用相应的doGet()、doPost()等方法处理请求。 -
销毁:当Web应用程序关闭或者Servlet不再需要时(比如服务器关闭或重新部署应用),容器会调用Servlet的
destroy()方法。在这个方法中,开发者可以释放资源,如数据库连接、清理临时文件等。
8.2 Servlet生命周期对应的方法
public void init(ServletConfig config) throws ServletException: 初始化方法,只在Servlet实例被创建后调用一次。public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException: 核心方法,每次请求到达时都由容器调用。默认实现会根据请求类型调用doGet()或doPost()等方法。protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException: 处理HTTP GET请求的方法,需要重写以提供具体功能。protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException: 处理HTTP POST请求的方法,同样需要重写。public void destroy(): 销毁方法,在Servlet生命周期结束时调用,用于释放资源。
在下面的代码和配置中,我们定义了一个名为LifeCycleServlet的Servlet类,并展示了其生命周期中的各个方法:init(), doGet()/doPost(), 和 destroy()。同时,在web.xml部署描述符文件中进行了相应的Servlet配置。
LifeCycleServlet.java
package com.org;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LifeCycleServlet extends HttpServlet {
// 初始化阶段
public void init() throws ServletException {
System.out.println("** 1、Servlet初始化 --> init()");
// 这里可以进行一些一次性设置操作,如读取配置、数据库连接等
}
// 处理HTTP GET请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {
handleRequest(req, resp);
}
// 处理HTTP POST请求
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException {
handleRequest(req, resp);
}
// 公共处理逻辑,将GET和POST请求统一处理
private void handleRequest(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("** 2、Servlet服务 --> doGet() 或 doPost()");
// 在这里添加实际业务逻辑
}
// 销毁阶段
public void destroy() {
System.out.println("** 3、Servlet销毁 --> destroy()");
// 这里可以释放资源,如关闭数据库连接、清理临时文件等
}
}
web.xml配置
<!-- 配置Servlet -->
<servlet>
<servlet-name>life</servlet-name>
<!-- 定义Servlet类所在的包名和类名 -->
<servlet-class>com.org.LifeCycleServlet</servlet-class>
<!-- 自动加载并初始化Servlet,值越小优先级越高,值为负数则不会自动加载 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 映射Servlet -->
<servlet-mapping>
<servlet-name>life</servlet-name>
<!-- 设置访问Servlet的URL路径 -->
<url-pattern>/LifeServlet</url-pattern>
</servlet-mapping>
在这个配置中,当Web应用启动时,由于设置了 <load-on-startup>1</load-on-startup>,Servlet容器会在应用程序启动时立即初始化并调用LifeCycleServlet的init()方法。之后,每当客户端通过/LifeServlet URL发起HTTP请求(无论是GET还是POST),都会触发LifeCycleServlet的相应服务方法,并执行业务逻辑。最后,在Web应用停止或重新部署时,Servlet容器会调用LifeCycleServlet的destroy()方法来清理资源。
8.3 Servlet的多线程机制
Servlet容器采用多线程机制处理并发请求。对于每一个新的HTTP请求,容器都会创建一个新的线程调用Servlet的service()方法。这意味着同一Servlet实例可能会被多个线程同时访问,因此Servlet设计必须考虑到线程安全问题。
由于Servlet是单实例多线程模型,即整个Web应用只有一个Servlet实例,所以在Servlet内部不应使用任何可变共享数据,除非确保了这些数据的线程安全性。为了处理这种情况,开发者应遵循以下原则:
- 尽量避免在Servlet中使用静态变量存储状态信息。
- 如果必须使用共享数据,请确保通过同步机制保护对这些数据的访问。
- 使用ThreadLocal存储每个线程特有的数据。
总之,Servlet的生命周期管理及多线程机制使得它能够高效地处理并发请求,但在编写Servlet代码时,必须谨慎处理线程安全问题,以保证应用在高并发环境下的正确运行。