Course topics

By WebNest Studio Engineering Team

Advanced Java Tutorial

Servlet Introduction and Lifecycle

A servlet is a Java class that runs inside a servlet container (such as Apache Tomcat, Jetty, or a full Jakarta EE server) and handles HTTP requests and responses. Servlets are the foundation of server-side Java web development — frameworks like Spring MVC, Struts, and even JSP itself compile down to servlets under the hood.

Unlike a plain Java class you instantiate yourself, a servlet's entire life — from creation to destruction — is managed by the container. You never call new on a servlet class in application code; you implement callback methods, and the container decides when to invoke them, based on incoming HTTP traffic and server startup/shutdown events.

The Servlet Interface and HttpServlet

Every servlet ultimately implements the javax.servlet.Servlet (or jakarta.servlet.Servlet, depending on the platform version) interface, but in practice almost nobody implements it directly. Instead, you extend HttpServlet, an abstract class that already implements generic request dispatching and exposes HTTP-specific methods like doGet, doPost, doPut, and doDelete that you override based on which HTTP methods your servlet should handle.

The Three Lifecycle Stages

The container manages a servlet through three well-defined stages, and each maps to a specific method you can override.

  • Loading and Instantiation — the container loads the servlet class and creates exactly one instance, typically on the first request (or at startup, if load-on-startup is configured).
  • init() — called exactly once, immediately after instantiation, before the servlet handles any request. Used for one-time setup like opening a database connection pool or reading configuration.
  • service() — called once per incoming request. HttpServlet's built-in service() inspects the HTTP method and dispatches to doGet(), doPost(), and so on automatically.
  • destroy() — called exactly once, when the container is shutting down the servlet (e.g., on application undeploy or server shutdown), giving you a chance to release resources.

Single Instance, Multiple Threads

A critical detail that trips up many developers: the container normally creates only one instance of each servlet class and reuses it for every request, dispatching concurrent requests to that single instance on separate threads. This means instance variables in a servlet are shared state across all users, and mutating them without synchronization is a common source of subtle, hard-to-reproduce bugs. Request-specific data should live in local variables inside doGet/doPost, never in servlet instance fields.

Example

A servlet showing init(), doGet(), and destroy() in the lifecycle

Java
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class GreetingServlet extends HttpServlet {

    private int requestCount = 0; // shared across all requests - handle with care

    @Override
    public void init() {
        System.out.println("GreetingServlet: init() called once at startup");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        requestCount++;
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h2>Hello from GreetingServlet</h2>");
        out.println("<p>This servlet has handled " + requestCount + " request(s).</p>");
        out.println("</body></html>");
    }

    @Override
    public void destroy() {
        System.out.println("GreetingServlet: destroy() called at shutdown");
    }
}
Output
Browser (each reload increments the counter):
Hello from GreetingServlet
This servlet has handled 1 request(s).
(reload again) This servlet has handled 2 request(s).

Server console on deploy:  GreetingServlet: init() called once at startup
Server console on undeploy: GreetingServlet: destroy() called at shutdown

Common Mistakes

  • Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads.
  • Overriding service() directly instead of doGet()/doPost() when extending HttpServlet, which bypasses the built-in HTTP-method dispatching.
  • Expecting init() to run on every request — it runs exactly once, right after the servlet is instantiated.
  • Forgetting that heavy work in init() (like opening many connections) delays the very first request if the servlet is lazily loaded rather than load-on-startup.

Exercise

Using the lesson's A servlet showing init(), doGet(), and destroy() in the lifecycle example, create a small implementation or design sketch that demonstrates A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends.. Add a test case or scenario that would catch Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads., and describe the expected result.

Show hints
  • Start with The Servlet Interface and HttpServlet and compare your approach with A servlet showing init(), doGet(), and destroy() in the lifecycle. Identify the decision or behavior the lesson recommends.
  • Walk through your scenario step by step. Confirm that it follows A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends. and that it would catch the failure described by Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads..

Quick quiz

1. Which choice is a mistake to avoid in Servlet Introduction and Lifecycle?

  • A. Overriding service() directly instead of doGet()/doPost() when extending HttpServlet, which bypasses the built-in HTTP-method dispatching.
  • B. A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends.
  • C. Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads.
Show answer

C. Avoid this approach: Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads. The lesson recommends: A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends.

Interview questions

A teammate is about to make this mistake: Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads. How would you diagnose and correct it using the lesson's guidance?

Show answer

A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends. Apply this in The Servlet Interface and HttpServlet; the example A servlet showing init(), doGet(), and destroy() in the lifecycle shows one way to do it. Avoid Storing per-request or per-user data in servlet instance variables, assuming each request gets a fresh object — the container reuses one instance across threads. and explain how your implementation or design prevents that failure.

Key Points to Remember

  • A servlet is a container-managed Java class that handles HTTP requests; HttpServlet is the class almost everyone extends.
  • Lifecycle order: instantiation, init() once, service() per request (dispatching to doGet/doPost), destroy() once at shutdown.
  • Normally one servlet instance serves all requests concurrently on different threads — avoid mutable shared instance state.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering