Day 12 · Week 2 · Web + Data

Servlets — concept walkthrough.

Today is intentionally light on code. We diagram the Servlet lifecycle, request dispatch, sessions, filters — so the moment Spring's DispatcherServlet shows up later, you recognize the shape it inherited.

~22 min readdiagrams firstno real coding today
You will never write a Servlet in a real Spring job. But Spring's entire web layer is one giant Servlet wrapped around your controllers. Knowing the shape of the thing — even without writing it — pays back forever.

Where we are in the story

Day 1 told you Servlets exist. Today we open the Servlet container's doors and see what's inside. By the end you'll be able to draw the lifecycle, the dispatch flow, and the role of filters from memory.

The Servlet lifecycle

A Servlet is a Java class that the container manages over time. It has three lifecycle phases:

init()once · on first requestservice() · doGet/doPostmany times · per request, per threaddestroy()once · on shutdownLong-lived single instance · concurrent calls share it
The three lifecycle phases. The container calls these for you — you just override the methods.
  1. init() — called once when the Servlet is first loaded (lazily on first request, or eagerly on startup if configured). Do one-time setup: open DB connections, read config, etc.
  2. service() — called for every request. Internally dispatches to doGet(), doPost(), etc. based on HTTP method. Many threads call this concurrently on the same instance.
  3. destroy() — called once when the container shuts down or unloads the Servlet. Clean up.
🧠 The threading consequence

Because the container creates one Servlet instance and runs many threads through it, your Servlet's instance fields are shared across requests. A field like private User currentUser; is a bug — request A sets it, request B reads A's user. This is exactly the same trap as Spring's stateless beans. Servlets must be stateless. Local variables (per-method, per-thread) are fine. Instance fields holding per-request data are not.

What a Servlet looks like (read-only)

@WebServlet("/users")
public class UsersServlet extends HttpServlet {

  private UserDao userDao;

  @Override
  public void init() {
    this.userDao = new UserDao(); // shared, immutable, stateless
  }

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
    String idStr = req.getParameter("id");
    User u = userDao.findById(Long.parseLong(idStr));
    res.setContentType("application/json");
    res.getWriter().write(toJson(u)); // you write Jackson manually
  }
}

Familiar? Your Spring controllers are descendants of this. @RestController + @GetMapping with @RequestParam + automatic JSON serialization is the same machinery, dressed nicer.

What the Servlet container does — recap

StepContainer's job
1Listen on port (8080 typically)
2Accept incoming TCP connection
3Read & parse raw HTTP bytes into HttpServletRequest
4Match URL → which Servlet (URL pattern in web.xml or @WebServlet)
5Pull a thread from the thread pool
6Call servlet.service(req, res)
7Take whatever the Servlet wrote into res and serialize as HTTP bytes back over TCP
8Return thread to pool

The container is doing a lot. The Servlet is doing one thing: compute the response.

Filters — interception around your Servlet

Sometimes you want behavior that runs around every request — authentication, logging, CORS, compression. Embedding that in every Servlet would be repetitive. The Servlet API solves this with Filters.

RequestAuthFilterLogFilterCorsFilterYour Servleteach filter calls chain.doFilter() to pass control onward
Filters form a pipeline around the Servlet. Each can short-circuit (e.g. an auth filter rejecting an unauthenticated request).
@WebFilter("/*")
public class AuthFilter implements Filter {
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest http = (HttpServletRequest) req;
    String token = http.getHeader("Authorization");
    if (!authService.isValid(token)) {
      ((HttpServletResponse) res).setStatus(401);
      return; // short-circuit — never calls chain.doFilter()
    }
    chain.doFilter(req, res); // pass to next filter or the Servlet
  }
}
📌 What this becomes in Spring

Spring Security is a chain of Servlet filters. OncePerRequestFilter, JwtAuthenticationFilter, CorsFilter — every one of them is a Servlet Filter. When you debug a Spring Security issue, you're debugging the same filter chain you saw on Day 1.

Sessions — making HTTP feel stateful

HTTP is stateless. But web apps need to know "this request is from the same user as the last one." The Servlet API offers HttpSession:

HttpSession session = req.getSession(true); // create if absent
session.setAttribute("userId", 42L);

// later request, same session cookie:
long userId = (Long) session.getAttribute("userId");

How? The container generates a JSESSIONID cookie on first session access, sets it on the response, and uses it on subsequent requests to find the same in-memory session object.

⚠️ Why modern APIs avoid sessions

Sessions store state on the server. If you scale horizontally to 5 instances, requests can hit different machines — the session is on machine 1 but the next request lands on machine 3. You must either pin requests (sticky sessions) or share session state via Redis. Either is operational pain.

JWT tokens (Day 8) avoid this entirely: state lives in the signed token. Any server with the key can verify. This is why modern REST APIs prefer tokens.

web.xml — the XML era

Old Servlet apps had a web.xml file mapping URL patterns to Servlets and Filters:

<!-- web.xml — historical -->
<servlet>
  <servlet-name>UsersServlet</servlet-name>
  <servlet-class>com.app.UsersServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>UsersServlet</servlet-name>
  <url-pattern>/users</url-pattern>
</servlet-mapping>

Servlet 3.0 (2009) added annotations like @WebServlet, @WebFilter, @WebListener — which is what Spring eventually built on. You will not write web.xml. Recognize it in legacy code, then close the file.

The bridge to Spring

Here's the most important sentence of the day:

🔑 Spring is a Servlet

Spring MVC ships one Servlet called DispatcherServlet. Tomcat treats it as a normal Servlet — calls service() on it for every request. Inside service(), Spring runs its own routing: examine @RequestMapping annotations, pick a controller method, parse @RequestBody via Jackson, call your method, serialize the return value back to JSON, write to the response.

Every Spring controller you'll ever write runs inside a Servlet. The Servlet model didn't go away — Spring just built a sophisticated dispatcher on top of it. Now you know.

Pause & reflect

Lock in today's learning

Just understanding. No coding.

  1. What three lifecycle methods does a Servlet have, and how often is each called?
  2. Why must Servlets be stateless? What's the threading reason?
  3. Describe what a Filter does and how it can short-circuit a request.
  4. How do sessions make HTTP feel stateful, and why do modern APIs avoid them?
  5. What is DispatcherServlet's relationship to Tomcat and to your @RestController?
  6. Spring Security's filter chain — what underlying Servlet API construct is it built on?

End of Day 12. Tomorrow: REST architecture — what 'RESTful' actually means.