Servlet 生命周期

init / service / destroy 三个阶段的调用时机与线程模型。

Beginner
Key Points容器加载类、调用一次 init(),之后每个请求一个线程调 service(),卸载时调一次 destroy()。

四个阶段

StageWhenHow many times
Load首次请求或容器启动时 load-on-startup1
init()实例创建后,处理请求前1
service()每个请求N
destroy()应用卸载或容器关闭1

完整示例

Java
import java.io.IOException;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/life", loadOnStartup = 1)
public class LifeCycleServlet extends HttpServlet {

    private long createdAt;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);              // 必须先调用,否则 getServletContext() 为 null
        createdAt = System.currentTimeMillis();
        log("init 完成,实例已创建");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        long alive = System.currentTimeMillis() - createdAt;
        resp.setContentType("text/plain;charset=UTF-8");
        resp.getWriter().printf("已运行 %d 毫秒%n", alive);
    }

    @Override
    public void destroy() {
        log("destroy 被调用,释放资源");
    }
}

loadOnStartup = 1 makes the container create the instance and call init() at startup instead of on the first request. Lower values start earlier; a negative value defers to first access.

service 如何分派

HttpServlet.service() inspects the request method and forwards to the matching doXxx. An unimplemented method yields 405 rather than 404 — the mapping is right, the action simply is not implemented.

Java
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    // 自定义分派:所有方法都走这里
    String method = req.getMethod();
    if ("GET".equals(method)) {
        doGet(req, resp);
    } else if ("POST".equals(method)) {
        doPost(req, resp);
    } else {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, method);
    }
}
If init() throws ServletException the container refuses to serve that servlet and later requests return 503. Keep initialisation fast; put connection pools and cache warm-up in a ServletContextListener instead of slowing startup here.

并发下的三条规则

  1. 实例字段只放只读的初始化结果(配置、连接池引用)
  2. 请求相关数据一律用局部变量,天然线程安全
  3. 必须共享的可变状态用 AtomicInteger / ConcurrentHashMap / 数据库

Friends & Partners