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() :容器将 request 和 response 对象作为参数,调用 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<br/>调用构造函数] C --> D[容器调用 init(ServletConfig)] D --> E[Servlet 就绪] E --> F{客户端请求到达?} F -- Yes --> G[容器创建 request/response 对象] G --> H[容器调用 service(request, response)] H --> I[service 方法根据请求方法<br/>调用 doGet/doPost/等] I --> J[执行业务逻辑<br/>生成响应] 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 应用的基础。

相关推荐
东方芷兰4 小时前
Agent 技术摘要 05 —— BP、SaaS、B2B、C2B、B2C
java·笔记·python·语言模型·自然语言处理
Chen—LSN4 小时前
数据结构——拿捏复杂度
java·c语言·开发语言·数据结构·经验分享·笔记·算法
一条破秋裤4 小时前
Linux 线程同步:条件变量
java·linux·jvm
步行cgn4 小时前
Spring AOP 切面的先后顺序详解
java·spring
ym hyd 1114 小时前
宝宝早教服务系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·spring boot
自强的小白5 小时前
微服务之间的依赖传递和调用(实体类的模块传递以及负载均衡的调用!)注册中心
java·微服务
她说彩礼65万5 小时前
C语言 单引号与双引号
java·服务器·c语言
知守观5 小时前
百万级数据导出OOM:POI的坑与EasyExcel的流式写入实战(附内存对比)
java·后端
anew___5 小时前
《从零手写操作系统 (19):Ext2文件系统实战——从内存到磁盘的跨越》
java·服务器·前端·数据库·算法
蜗牛互联网5 小时前
Computer Use公共预览的安全设计:应用、动作与数据三维门禁
java·人工智能·后端·安全·策略模式