LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2119 min read

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 You Will Learn
  • 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.

@Controller
public class BookController {
// handler methods go here
}
@Controller vs @RestController

@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";
}
}
Resulting Routes

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";
}
Automatic Type Conversion

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

Avoid These 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.

Next Lesson →

Model, View, and ModelAndView