You send a form on a website. Somewhere on a server, something has to catch that request, figure out what to do with it, and send back a response. In Java, that "something" is often a servlet.
If you've been staring at web.xml files or annotation-based mappings wondering how the pieces connect, this guide walks you through it — from the moment a servlet loads to the moment it dies.
The Servlet Lifecycle: What Happens Behind the Scenes
Every servlet lives inside a web container. Tomcat and Jetty are the two you'll run into most often. The container isn't just hosting your code — it's actively managing the servlet's entire life, from birth to shutdown.
Here's how that plays out.
1. Initialization
Your servlet class extends HttpServlet or implements the Servlet interface. When the container starts up, or when your servlet gets hit for the first time, the container loads the class, creates an instance, and calls init(). This step only happens once. After that, the same instance handles every request that comes its way.
2. Handling Requests
This is where the real work starts. The service() method takes two objects — HttpServletRequest and HttpServletResponse — and uses them to figure out what the client wants and how to respond. When a browser fires off an HTTP request, the container checks the URL, matches it against your mappings (either in web.xml or via annotations like @WebServlet), and routes the request to the right servlet.
3. Processing the Request
Inside service(), you're usually pulling parameters, running some business logic, maybe hitting a database, and putting together a response. Servlets don't have to do this alone, either — they often team up with JSPs or EJBs to split the work.
4. Generating the Response
Once processing wraps up, the servlet builds the actual HTTP response. That means setting headers, writing content (HTML, JSON, whatever the client needs), and deciding whether to send the response directly or forward the request somewhere else entirely.
5. Destruction
When the container shuts down or the app gets undeployed, it calls destroy(). This is your cleanup moment — closing connections, releasing resources, tying up loose ends.
Seeing It in Code: Three Practical Examples
Theory only gets you so far. Let's look at three servlets that show up constantly in real applications.
Example 1: The Classic Hello World Servlet
Every Java developer writes this one first. It's simple, but it shows you the basic shape every servlet follows.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class HelloWorldServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>Hello, World!</h1>");
out.println("</body></html>");
}
}
Notice doGet() instead of service() directly. HttpServlet already handles the routing between GET, POST, and the rest — you just override the method that matches the HTTP verb you care about.
Example 2: A User Registration Servlet
Forms need somewhere to go. This servlet catches a POST request, pulls out the username and password, and hands them off for validation and storage.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class RegistrationServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Retrieve form parameters
String username = request.getParameter("username");
String password = request.getParameter("password");
// Validate input
// Process registration (store user information in database)
// Generate response
}
}
This is the bread and butter of most web apps — grab the data, check it, save it, respond.
Example 3: A File Upload Servlet
File uploads trip people up more than they should. Here's the pattern that handles it cleanly.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class FileUploadServlet extends HttpServlet {
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Retrieve uploaded file(s)
Part filePart = request.getPart("file");
InputStream fileContent = filePart.getInputStream();
// Process file (e.g., store it on the server)
// Generate response
}
}
getPart() gives you access to the uploaded file as a stream. From there, you decide where it lives — disk, cloud storage, wherever your app needs it.
Why This Still Matters
Frameworks like Spring sit on top of servlets, not instead of them. Every controller you write in Spring MVC eventually gets translated down into servlet calls. Understanding this layer means you're not just following a framework's conventions blindly — you actually know what's happening underneath.
That's the difference between writing code that works and writing code you actually understand.