The labs on this site need JavaScript to run. The same content is written out below.
Learn › Spring › Web MVC
Servlets
How an HTTP request reaches Java code
What a browser sends is an HTTP request made of text. A servlet container such as Tomcat reads that text, turns it into Java objects, and calls the method of the servlet that is mapped to the path. A servlet is a Java class that "receives a request object and writes to a response object".
Checked against real execution · 48 different requests were sent to Tomcat 11.0.24 and Spring Boot 4.1.1 and checked against the results
Concepts in this lab
- Servlet and servlet container
- A servlet is the Java standard interface that receives an HTTP request, processes it, and produces a response. A servlet container (such as Tomcat) accepts network connections, parses requests, calls the right servlet, and manages the servlet lifecycle and the request-handling threads. Spring MVC runs on top of a single servlet called DispatcherServlet.
- DispatcherServlet and the front controller pattern
- This is the servlet in Spring MVC that receives every HTTP request first. It coordinates the whole flow: it finds and calls the controller that handles the request, and it turns the result into a response. A structure like this, in which a single entry point receives all requests, does the common processing, and then hands each request to the part responsible for it, is called the front controller pattern.
- Embedded server
- This is a web server (Tomcat by default) that is included inside a Spring Boot application and runs together with it. You can run the application with a single java -jar command, without the steps of installing a server separately and deploying the application to it.
- Web server and application server
- A web server is a program that receives HTTP requests and either responds with static files such as images and HTML or forwards the requests to another server (Nginx, Apache). An application server (web application server) is a program that runs program code and builds a result for each request (Tomcat). A common setup puts a web server in front and passes requests on to the application server.
- Tomcat's threading model
- Tomcat manages its request processing threads in a pool, and one thread takes one request and handles it until the response is finished. The number of requests it can handle at the same time is limited by the maximum number of threads (200 by default), and when all threads are in use, new requests wait. A thread stays occupied even while the request is waiting on a database or an external call.
- HTTP methods
- The part of an HTTP request that indicates the action the request wants to perform on a resource. GET means read, POST means create or a processing request, PUT means replace the whole resource, PATCH means modify part of it, and DELETE means delete. Use each one according to its meaning so that HTTP behavior such as caching and retries works as expected.
Try these
- Default settings — GET /hello twice. The servlet object is created only once, at the first request.
- Creating it at server startup — The setup work is done at server startup, not at the first request.
- A POST arrives but there is only doGet — The request reaches the servlet but is rejected with 405.
- A path that nothing is mapped to — No servlet matches, so the response is 404.
- In Spring MVC — There is only one servlet, DispatcherServlet, and it calls the controller.
Running with the default settings
- The server starts. Tomcat listens for connections on port 8080 and builds a table of "which servlet handles which path". It registers HelloServlet, which is annotated with @WebServlet("/hello"), as the servlet for /hello. The servlet object has not been created yet.
- Request #1: The browser connects to the server and sends an HTTP request. What it sends is a few lines of text, like the ones shown on the screen.
- Tomcat assigns a thread from its thread pool and parses the text it received into an HttpServletRequest object. It also creates an empty HttpServletResponse object to hold the response. The servlet does not have to parse the text itself. It only has to read the values from this object.
- Tomcat looks in the table for the servlet mapped to the path /hello. It is HelloServlet.
- Tomcat creates the HelloServlet object at the first request. The developer does not create it with new. The container does. This one object is used until the server shuts down.
- HelloServlet.init() is called once. This is where the servlet does the setup it needs before it receives requests.
- Tomcat calls service(request, response) of HelloServlet. service() looks at the HTTP method of the request and calls doGet().
- doGet() runs the code that we wrote. It reads name from request and writes the status and the body to response.
- The method of the servlet has finished. Tomcat turns the content of the HttpServletResponse into the text of an HTTP response and sends it to the browser. It discards the request and response objects used for this request and returns the thread to the pool. The HelloServlet object stays as it is and waits for the next request.
- Request #2: The browser connects to the server and sends an HTTP request. What it sends is a few lines of text, like the ones shown on the screen.
- Tomcat assigns a thread from its thread pool and parses the text it received into an HttpServletRequest object. It also creates an empty HttpServletResponse object to hold the response. The servlet does not have to parse the text itself. It only has to read the values from this object.
- Tomcat looks in the table for the servlet mapped to the path /hello. It is HelloServlet.
- The HelloServlet object already exists. Tomcat does not create a new one and uses that object again. It does not call init() again either.
- Tomcat calls service(request, response) of HelloServlet. service() looks at the HTTP method of the request and calls doGet().
- doGet() runs the code that we wrote. It reads name from request and writes the status and the body to response.
- The method of the servlet has finished. Tomcat turns the content of the HttpServletResponse into the text of an HTTP response and sends it to the browser. It discards the request and response objects used for this request and returns the thread to the pool. The HelloServlet object stays as it is and waits for the next request.
- The server shuts down. Tomcat calls destroy() of HelloServlet once and then discards the object. This is the chance to clean up the resources that were opened.
Result of this run
- A servlet is a Java class that receives one HTTP request and produces a response, and a servlet container (Tomcat) is the program that creates, calls, and destroys that servlet. The container accepts connections, turns the HTTP text into objects, and assigns threads, so the developer writes only the code that "reads from the request and writes to the response".
- The number of requests was 2, but there was only one servlet object. What is created anew for each request is the request and response objects. Several threads use the one servlet object at the same time, so you must not store per-request values in the fields of the servlet (the same reason as for singleton beans in Spring).
- When the servlet is not created in advance, object creation and init() happen while the first request is being handled, so only the first request is slow. If the setup work is heavy, create the servlet in advance at server startup.
Code
@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
@Override
public void init() { // once, right after the object is created
// setup work
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String name = req.getParameter("name"); // read from the request
resp.setContentType("text/plain; charset=UTF-8");
resp.getWriter().write("Hello, " + name); // write to the response
}
@Override
public void destroy() { } // once, when the server shuts down
}
Assumptions of this model
The model assumes that the servlet container is Tomcat and that requests arrive one at a time, in order. Three points follow the Jakarta Servlet specification and the documentation of HttpServlet: only one servlet object is created per servlet declaration, init and destroy are each called once, and a doPost that is not overridden returns 405. Only the main lines of the HTTP request and response text are shown. For the 36 combinations with a directly written servlet, requests were sent to a real Tomcat 11.0.24, and for the 12 combinations on the Spring MVC side, to a real Spring Boot 4.1.1, and it was confirmed that the response code, the counts of object creation, init, and request handling calls, and the time when the object was created match the screen. In this process, two points on the Spring side were corrected to match reality. The DispatcherServlet object is created when the server starts, not at the first request (only init happens at the first request), and the 404 and 405 responses are dispatched once more to /error, so there are two request handling calls per request. Among the entries for where the response was produced, "Default servlet of Tomcat" and "Default doPost of HttpServlet" were inferred from the response code and were not observed directly.
Previous lab: Connection pool and OSIV · Next lab: Spring MVC request flow