Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Spring

Controllers

Which class is called a controller?

A controller is a Java class declared with class. It differs from an ordinary class in two places. @RestController above the class is a marker that tells Spring to create an object of this class and treat it as a class that handles web requests, and @GetMapping("/hello") above a method is a registration that hands a GET request the browser sends to the path /hello to this method. A controller is not a servlet. A request is received by one servlet (DispatcherServlet) that Spring Boot registered with Tomcat, and DispatcherServlet calls the method of the controller. You remove these two one at a time and send the same request, to see whether Spring creates the object and calls the method.

Checked against real execution · Started all 8 combinations on Spring Boot 4.1.1 with real component scanning, sent requests, and checked against the results

Concepts in this lab

Controller class
A controller class is a Java class in Spring MVC that has methods that handle web requests. Unlike a servlet, it does not have to extend a particular class or implement an interface, and you mark it as a controller by putting @Controller or @RestController above the class. Spring scans the classpath (component scanning), finds the classes with the marker, and creates and keeps their objects, so the developer does not create them with new. A request is received by DispatcherServlet, which looks at the path written above a method (@GetMapping and so on) and calls the method of the matching controller. Paths are written on each method (above the class you can write only a shared prefix), so one class can hold several paths.
@RestController
@RestController is an annotation that marks a class as a controller. It combines @Controller and @ResponseBody, so the value that a request-handling method of this class returns is written directly to the response body without going through a view. @Controller is a specialization of @Component, so Spring finds this class by component scanning and registers it as an object. Without this annotation, Spring does not find the class as a controller, so it does not receive requests even if its methods have @GetMapping.
@GetMapping
@GetMapping is an annotation that hands HTTP GET requests to a particular method. You write the path (for example "/hello") in the parentheses, and it is a shortcut form of @RequestMapping(method = RequestMethod.GET). Spring reads this annotation on the methods of the classes it found as controllers and builds a path table that pairs paths with methods. There are shortcut forms made in the same way: @PostMapping, @PutMapping, @DeleteMapping, and @PatchMapping.
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.

Try these

Running with the default settings

  1. The server starts. Spring Boot starts Tomcat and registers one servlet, called DispatcherServlet, with Tomcat. We did not write this servlet. Spring provides it, and it handles requests for every path ("/").
  2. Spring scans the classes of the project and looks for classes with @RestController. It finds it on the HelloController class, so Spring creates an object of this class and keeps it. This object is created when the server starts, before the first request arrives.
  3. Spring looks for methods with @GetMapping in the HelloController class, and writes the path and the method written there into the path table of Spring.
  4. You type localhost:8080/hello in the address bar of the browser, and the browser sends a GET /hello request to the server.
  5. Tomcat receives the request. The only servlet registered with Tomcat is DispatcherServlet, and it handles every path, so Tomcat passes the request to DispatcherServlet. Nothing has reached the classes we wrote yet.
  6. DispatcherServlet looks for the path /hello in the path table of Spring. The HelloController.hello method is written there.
  7. DispatcherServlet calls the method it found. The object that this method belongs to was created by Spring when the server started. The code we wrote has no line new HelloController(), and no line that calls the hello() method. The text this method returns is "Hello".
  8. The browser receives a 200 response. The text the method returned becomes the response body, so the body is "Hello".

Result of this run

Code

// Marker: a class that handles web requests. Spring creates its object
@RestController
public class HelloController {
    // Registration: hand GET requests for this path to this method
    @GetMapping("/hello")
    public String hello() {
        return "Hello";
    }
}

Assumptions of this model

The result was obtained by running Spring Boot 4.1.1 (Spring Framework 7.0.9, embedded Tomcat 11) on JDK 21. The model assumes that the browser sends one GET request per path, one after another. The localhost:8080 in the paths is an example of starting Spring Boot on its default port on my own computer. For all 8 combinations, the same source as the code on the screen (with only the lines that count objects and runs added) was compiled in the package app, and a new Java process was started with a start class annotated with @SpringBootConfiguration, @EnableAutoConfiguration, and @ComponentScan. The controller was not registered by hand but found by Spring scanning the classes, and the requests were sent over real HTTP after the server had started. What was checked is the number of controller objects that Spring created (right after the server started and after the requests), the path table (the entries of this class among getHandlerMethods of RequestMappingHandlerMapping), the status code and body for each path, and the number of method runs. That DispatcherServlet is the only servlet registered with Tomcat and handles the path "/", that the controller class is not of the Servlet type, and that the method did not run for a 404 response were also checked by execution. What the documentation defines is the DispatcherServlet section of Web MVC in the Spring Framework documentation (that DispatcherServlet is the one servlet that receives all requests and the actual work is done by delegate components, and that in Spring Boot the servlet declaration is found in the Spring configuration and registered with the container), the Declaration section of Annotated Controllers (that controller objects are found by classpath scanning, and that @RestController combines @Controller and @ResponseBody), the Mapping Requests section (shortcut annotations such as @GetMapping), and the API documentation of @RestController, @Controller, and @GetMapping. That the object is created when the server starts and the path table is built then, and that a 404 response is returned when there is no path, are not defined by the documentation but are behaviors observed in the default settings of this version. The code on the screen leaves out the package and import lines, and it assumes that the controller is in the same package as the start class (or below it). For registering paths, only @GetMapping was covered, and @RequestMapping, @PostMapping, and @Controller were not. That DispatcherServlet of Spring Boot is initialized when the first request arrives and not when the server starts was observed, but it is not included in the steps of this lab, and it is covered in the in-depth lab "Spring MVC request flow".

Previous lab: Servlets · Next lab: Configuration files

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these