Controllers & Request Mappings
Learn how @Controller and @RequestMapping connect incoming URLs to handler methods in Spring MVC.
Introduction
In the last lesson you saw that DispatcherServlet receives every incoming HTTP request and hands it off to a handler method based on the URL. That handler method lives inside a controller class, and the mapping between a URL and a method is declared with annotations. In this lesson, you will write your first real controllers and learn how @Controller and @RequestMapping work together to route requests to the right piece of Java code.
- What a controller is in the Spring MVC architecture.
- How @Controller marks a class as a request handler.
- How @RequestMapping binds a URL (and HTTP method) to a method.
- The shortcut annotations @GetMapping, @PostMapping, and friends.
- How to read path variables and query parameters from a request.
What Is a Controller?
A controller is a Spring-managed bean whose job is to receive an incoming HTTP request, do the minimum work needed to figure out what the user wants, delegate the real work to a service layer, and decide which view (or response body) to send back. Controllers should stay thin - request parsing and response selection, not business logic. That logic belongs in a @Service bean the controller calls into.
The @Controller Annotation
@Controller is a stereotype annotation, just like @Component and @Service, so any class marked with it is picked up by component scanning and registered as a bean. What makes @Controller special is that DispatcherServlet specifically looks for beans marked with it (or @RestController) when deciding which class can handle a request.
@Controllerpublic class BookController {
// handler methods go here}@Controller methods normally return a view name (like a Thymeleaf or JSP template) unless the method is also annotated with @ResponseBody. @RestController is @Controller + @ResponseBody applied to every method, so it always writes the return value directly into the HTTP response body - the standard choice for JSON APIs.
Mapping Requests with @RequestMapping
@RequestMapping tells Spring which URL path (and optionally which HTTP method) a method should handle. It can be applied at the class level to set a shared base path, and at the method level to add the specific path for that handler.
@Controller@RequestMapping("/books")public class BookController {
@RequestMapping(value = "/list", method = RequestMethod.GET) public String listBooks(Model model) { model.addAttribute("books", List.of("Effective Java", "Clean Code")); return "book-list"; // resolves to a view template named book-list }}A GET request to /books/list is now routed to listBooks(). The class-level @RequestMapping("/books") is combined with the method-level path, so you never have to repeat the shared prefix on every method.
HTTP Method Shortcuts
Writing method = RequestMethod.GET on every mapping gets repetitive, so Spring provides shortcut annotations that combine @RequestMapping with a specific HTTP method: @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, and @PatchMapping. These are the annotations you will use in almost all real code.
@Controller@RequestMapping("/books")public class BookController {
@GetMapping("/list") public String listBooks(Model model) { model.addAttribute("books", List.of("Effective Java", "Clean Code")); return "book-list"; }
@GetMapping("/{id}") public String bookDetails(@PathVariable Long id, Model model) { model.addAttribute("bookId", id); return "book-details"; }
@PostMapping("/add") public String addBook(@RequestParam String title) { // save the book, then redirect return "redirect:/books/list"; }}Click Run to see what this code prints.
Path Variables and Query Parameters
@PathVariable extracts a value from a segment of the URL path itself (like the 42 in /books/42). @RequestParam extracts a value from the query string or form data (like ?title=Dune). Both can be marked required or optional and given default values.
@GetMapping("/search")public String search( @RequestParam(name = "title", required = false, defaultValue = "") String title, @RequestParam(name = "page", defaultValue = "1") int page, Model model) {
model.addAttribute("title", title); model.addAttribute("page", page); return "search-results";}Notice that page above is declared as an int, not a String. Spring automatically converts request parameters and path variables to the declared Java type - int, Long, boolean, and even enums - and returns a 400 Bad Request if the conversion fails.
Common Mistakes
- Forgetting @Controller entirely and wondering why DispatcherServlet never finds the handler.
- Mixing up @PathVariable and @RequestParam - a value embedded in the path is a path variable, a value after ? is a request parameter.
- Not specifying an HTTP method and letting a mapping accidentally accept GET, POST, and everything else.
- Putting business logic directly inside controller methods instead of delegating to a service bean.
- Using mismatched names between the URL template placeholder (e.g. {id}) and the @PathVariable name without an explicit value, causing a runtime error.
Best Practices
- Prefer the HTTP method shortcuts (@GetMapping, @PostMapping, ...) over raw @RequestMapping for readability.
- Set a shared base path with a class-level @RequestMapping to avoid repeating prefixes.
- Keep controller methods short - parse input, call a service, choose a view or response.
- Use @PathVariable for identifying a resource (its id) and @RequestParam for optional filters or options.
- Group related endpoints (all book operations, all order operations) into the same controller class.
Frequently Asked Questions
Yes. @GetMapping({"/list", "/all"}) accepts an array of paths, and any of them will route to that method.
Spring fails to start the application context and throws an ambiguous mapping exception, since it cannot decide which method should handle the request.
No, it is optional. Without it, method-level mappings are resolved against the application's root context path directly.
Yes - it accepts headers, consumes, and produces attributes so you can further restrict a mapping, for example to only match requests with a JSON body.
Key Takeaways
- @Controller marks a class as a Spring MVC request handler and registers it as a bean.
- @RequestMapping binds a URL path (and optionally an HTTP method) to a handler method.
- @GetMapping, @PostMapping, and similar annotations are shortcuts for the common HTTP methods.
- @PathVariable reads values from the URL path; @RequestParam reads values from the query string or form data.
- Spring automatically converts path variables and request parameters to the declared Java parameter type.
Summary
Controllers and request mappings are how a raw HTTP request becomes a call to a specific Java method with typed parameters. Next, you will look at what a handler method does with that request once it has been routed - specifically, how Model and view names carry data from the controller to the page the user sees.