Servlet 生命周期详解

Servlet 是 Java EE 规范中用于扩展 Web 服务器功能的核心组件。理解 Servlet 的生命周期对于开发健壮、高效的 Web 应用至关重要。Servlet 的生命周期由 Web 容器(如 Tomcat, Jetty)严格管理,主要包含三个阶段:初始化(Initialization)请求处理(Request Handling)销毁(Destruction)

1. 加载、实例化与初始化 (init())

  • 触发时机:当 Web 应用启动时,或者当客户端首次请求访问该 Servlet 时(取决于配置)。
  • 过程
    1. 加载 :Web 容器根据部署描述符(web.xml)或注解(如 @WebServlet)定位 Servlet 类。
    2. 实例化 :容器调用 Servlet 类的无参构造函数,创建一个 Servlet 实例。注意: 通常一个 Servlet 类在整个 Web 应用中只被实例化一次(单例模式),处理后续所有请求。
    3. 初始化 :在实例化后,容器立即调用该实例的 init(ServletConfig config) 方法。
      • ServletConfig 对象:包含 Servlet 的初始化参数(在 web.xml 中配置或通过 @WebInitParam 注解指定)和 ServletContext 引用。
      • 用途 :开发者可以在此方法中进行一次性的初始化工作,例如:
        • 建立数据库连接池
        • 加载配置文件
        • 初始化资源
        • 执行其他只需执行一次的设置任务
  • 关键点
    • init() 方法只会在 Servlet 实例的生命周期中被调用一次。
    • 如果初始化失败(例如抛出 ServletException),该 Servlet 实例将不会被投入使用,容器可能会记录错误。
java 复制代码
public class MyServlet extends HttpServlet {

    private SomeResource resource; // 示例资源

    @Override
    public void init() throws ServletException {
        // 通常重写无参的 init() 方法更方便
        super.init();
        // 执行初始化逻辑,例如:
        resource = initializeResource(); // 初始化资源
    }

    // ... 其他方法
}

2. 请求处理 (service() -> doGet(), doPost(), etc.)

  • 触发时机:每当有新的客户端请求(HTTP 请求)被映射到该 Servlet 时。
  • 过程
    1. 接收请求:Web 容器接收到一个指向该 Servlet 的 HTTP 请求。
    2. 创建对象 :容器创建代表请求的 HttpServletRequest 对象和代表响应的 HttpServletResponse 对象。
    3. 调用 service() :容器将 requestresponse 对象作为参数,调用 Servlet 实例的 service(HttpServletRequest req, HttpServletResponse resp) 方法。
    4. 分发请求 :在 HttpServlet 类(绝大多数 Servlet 的基类)中,service() 方法会根据 HTTP 请求的方法(GET, POST, PUT, DELETE 等)调用相应的 doXxx() 方法。
      • 例如,GET 请求会调用 doGet(HttpServletRequest req, HttpServletResponse resp)
      • POST 请求会调用 doPost(HttpServletRequest req, HttpServletResponse resp)
    5. 执行业务逻辑 :开发者通常在 doGet(), doPost() 等方法中编写处理请求的核心业务逻辑:
      • 读取请求参数
      • 访问数据库
      • 执行业务计算
      • 生成响应内容(HTML, JSON, XML 等)
      • 设置响应头、状态码
      • 将响应发送回客户端
  • 关键点
    • 此阶段是 Servlet 生命周期中最活跃的部分,会被反复调用以处理多个并发请求。
    • 线程安全 :由于 Servlet 是单例的,其 service()doXxx() 方法会被多个线程同时访问。开发者必须谨慎处理实例变量(成员变量)的并发访问,避免线程安全问题。 通常建议使用局部变量或线程安全的结构。
    • 请求对象 (request) 和响应对象 (response) 的生命周期仅限于单个请求的处理过程。
java 复制代码
public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 处理 GET 请求的逻辑
        String param = request.getParameter("name"); // 获取请求参数
        // ... 业务处理 ...
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>Hello, " + param + "!</body></html>"); // 生成响应
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 处理 POST 请求的逻辑
    }
}

3. 销毁 (destroy())

  • 触发时机
    • Web 应用被停止或卸载时(例如服务器关闭、应用重新部署)。
    • Servlet 长时间空闲后(取决于容器实现,不常见)。
  • 过程 :容器在回收 Servlet 实例之前,会调用其 destroy() 方法。
  • 用途 :开发者可以在此方法中进行清理工作,释放 init() 方法中分配的资源,例如:
    • 关闭数据库连接
    • 保存状态到持久存储
    • 释放文件句柄、网络连接等
  • 关键点
    • destroy() 方法在 Servlet 生命周期中只被调用一次。
    • 一旦 destroy() 被调用,该 Servlet 实例将不再处理任何新的请求。
    • 容器保证在调用 destroy() 之前,所有正在该 Servlet 上执行的 service() 线程(处理请求的线程)都已完成或超时终止。
    • 在此方法中,可以访问 ServletContext 获取应用级别的信息,但不能再访问请求或响应对象。
java 复制代码
public class MyServlet extends HttpServlet {

    private SomeResource resource; // 在 init() 中初始化

    @Override
    public void destroy() {
        // 执行清理逻辑,例如:
        if (resource != null) {
            resource.cleanup(); // 清理资源
        }
        super.destroy();
    }
}

生命周期流程图

graph LR A[Web 应用启动/首次请求] --> B[容器加载 Servlet 类] B --> C[容器实例化 Servlet
调用构造函数] C --> D[容器调用 init(ServletConfig)] D --> E[Servlet 就绪] E --> F{客户端请求到达?} F -- Yes --> G[容器创建 request/response 对象] G --> H[容器调用 service(request, response)] H --> I[service 方法根据请求方法
调用 doGet/doPost/等] I --> J[执行业务逻辑
生成响应] J --> K[发送响应] K --> F F -- No --> L{应用停止/卸载?} L -- Yes --> M[容器调用 destroy()] M --> N[释放资源] N --> O[Servlet 实例被垃圾回收] L -- No --> F

总结

Servlet 的生命周期是一个清晰定义的模型:

  1. init() 一次:用于初始化。
  2. service()/doXxx() 多次:用于处理请求(核心)。
  3. destroy() 一次:用于清理。

Web 容器负责精确管理这个生命周期。开发者应遵循这个模型,在正确的方法中编写相应的代码(初始化在 init(),业务逻辑在 doGet()/doPost() 等,清理在 destroy()),并特别注意 Servlet 单例特性带来的线程安全问题。理解并合理利用 Servlet 生命周期是构建稳定 Java Web 应用的基础。

相关推荐
上78将1 小时前
JVM回收垃圾机制
java·开发语言·jvm
Evan芙1 小时前
shell编程求10个随机数的最大值与最小值
java·linux·前端·javascript·网络
BD_Marathon1 小时前
【IDEA】IDEA的详细设置
java·ide·intellij-idea
未来coding1 小时前
Spring AI ChatModel API 详解【基于官方文档】
java·后端·spring
忘记9261 小时前
重复注解的机制是什么
java
刘一说1 小时前
JDK 25新纪元:技术革新与老项目迁移的冷思考
java·开发语言
小帅学编程1 小时前
Java基础
java·开发语言
思密吗喽1 小时前
如何完全清除Node.js环境重装 Node.js彻底卸载指南
java·开发语言·node.js·毕业设计·课程设计
summer__77771 小时前
38-第七章:集合(7.1-7.4)
java