How Java Powers the Web
How Java Powers the Web
Before writing a single line of servlet code it is worth spending time understanding the infrastructure it runs on. Java has powered enterprise web applications for over two decades — not because of syntax sugar, but because of a well-defined, container-managed runtime that handles concurrency, lifecycle, and protocol translation for you. This lesson maps out that infrastructure so every subsequent concept lands in context.
The HTTP Request/Response Model
Every web interaction — from loading a page to submitting a form to calling a REST endpoint — follows the same fundamental pattern: a client sends an HTTP request and a server returns an HTTP response. HTTP is a stateless, text-based protocol. Each exchange is independent; the server retains no memory of previous requests unless you explicitly build that layer (cookies, sessions, tokens).
An HTTP request consists of:
- Method — the intent:
GET(read),POST(submit/create),PUT/PATCH(update),DELETE(remove). - Request-URI — the resource path, e.g.
/products/42. - Headers — metadata:
Content-Type,Authorization,Accept, etc. - Body — optional payload (form fields, JSON, binary data).
GETandDELETEconventionally have no body.
An HTTP response consists of:
- Status line — a three-digit code:
200 OK,301 Moved Permanently,404 Not Found,500 Internal Server Error. - Headers —
Content-Type,Cache-Control,Set-Cookie, etc. - Body — the payload: HTML, JSON, an image, a file download.
Web Servers vs. Servlet Containers
People use the terms "web server" and "application server" loosely, but there is a meaningful distinction that directly affects how you structure Java web applications.
A web server (Nginx, Apache httpd) is optimised for serving static content — HTML files, images, CSS, JavaScript — as fast as possible. It speaks HTTP fluently and can act as a reverse proxy, but it has no native ability to execute Java code or manage application components.
A servlet container (also called a web container) is a Java runtime that:
- Accepts incoming HTTP connections.
- Parses the raw HTTP bytes into objects your code can use (
HttpServletRequest,HttpServletResponse). - Maps the request URL to the correct servlet class.
- Creates and manages servlet instances, including their entire lifecycle.
- Manages thread pools so multiple requests are handled concurrently.
- Serialises the response object back into HTTP bytes and sends it to the client.
Apache Tomcat is the reference implementation and by far the most widely deployed servlet container. It is lightweight, embeddable (Spring Boot embeds it by default), and implements the Jakarta Servlet specification (formerly javax.servlet before the namespace moved to jakarta.*).
javax.servlet). Tomcat 10.x+ implements Servlet 5.0+ (jakarta.servlet) following Oracle's transfer of Java EE to the Eclipse Foundation. If you are starting a new project today, use Tomcat 10+ and Jakarta EE 10 imports. Legacy codebases on Tomcat 9 still use the javax namespace. This tutorial uses the modern jakarta.* namespace throughout.
Where Servlets Fit
A servlet is simply a Java class that the container calls when a matching HTTP request arrives. It sits between the raw network layer (Tomcat) and your application logic (services, repositories, domain objects). Think of it as the HTTP entry point — the place where an HTTP request becomes a Java method call.
The flow looks like this:
Your code never deals with TCP sockets, HTTP parsing, or thread management. The container handles all of that. You implement business logic and output — the part only you know.
The Servlet Specification
A servlet is not a framework concept — it is a formal standard. The Jakarta Servlet Specification defines the jakarta.servlet.Servlet interface, the lifecycle methods the container must call, the threading model, session semantics, and the deployment descriptor format (web.xml). Any compliant container — Tomcat, Jetty, WildFly, GlassFish — must honour this contract. Code written to the spec is portable across containers without modification.
The class hierarchy you will use most is:
You extend HttpServlet and override only the HTTP-method handlers you need: doGet(), doPost(), doPut(), doDelete(). The container calls the right one based on the incoming request method.
Here is the minimal skeleton — you will flesh this out in the next lessons:
@WebServlet annotation replaces web.xml mapping. Prior to Servlet 3.0, every servlet had to be declared and mapped in a WEB-INF/web.xml deployment descriptor. The annotation is cleaner and keeps the URL mapping next to the class it belongs to. Both mechanisms still work; frameworks like Spring MVC typically use programmatic registration instead.
How Tomcat Handles Concurrency
Tomcat maintains a thread pool (default: up to 200 threads). When a request arrives it is dispatched to an available thread. That thread calls your servlet's doGet() (or doPost() etc.), waits for it to return, then the response is flushed and the thread returns to the pool.
The critical implication: the same servlet instance handles many concurrent requests on different threads simultaneously. The container creates one servlet instance per mapping (by default) and reuses it for every request. This means:
- Instance variables on your servlet are shared across all threads — a notorious source of race conditions.
- All request-scoped state must live in local variables inside the method, or on the request/session objects.
- If you store a counter, cache, or any mutable state in a servlet field, you must synchronise it explicitly.
this.currentUser = req.getParameter("user") inside doGet() is a race condition: thread A's value will be overwritten by thread B's before thread A finishes using it. Keep state in HttpSession, request attributes, or method-local variables.
The Bigger Picture: MVC and Beyond
Raw servlets are the foundation, but most modern Java web applications layer a framework on top. Spring MVC's DispatcherServlet is itself a single servlet that receives every request and delegates to your @Controller methods. Jakarta Faces (JSF) drives its lifecycle through a FacesServlet. Understanding servlets means you understand what those frameworks are doing for you — and you can debug them when the abstraction leaks.
Summary
HTTP is a stateless request/response protocol. Tomcat (the servlet container) handles the network layer — parsing HTTP bytes, managing threads, and mapping URLs — so your code focuses purely on application logic. A servlet is a Java class that plugs into this container via the Jakarta Servlet specification; you extend HttpServlet, override the relevant HTTP-method handler, read from HttpServletRequest, and write to HttpServletResponse. Because one servlet instance serves many concurrent threads, you must never store per-request state in instance fields. With this mental model in place, you are ready to set up a real Tomcat project and write your first working servlet in the next lesson.