The labs on this site need JavaScript to run. The same content is written out below.
Learn › Spring › Web MVC
Spring MVC request flow
How one request reaches the controller, and how it goes back
MVC is a pattern that divides a program into the model (the data), the view (the display), and the controller (the part that receives and handles a request), and Spring MVC handles web requests in this way. An HTTP request passes through the filters, DispatcherServlet, and the interceptors in turn before it arrives at the controller, and it becomes a response by going back the same way in reverse. This lab shows at which step along the path of one request the controller, the model, and the view each do their work, and which of them are absent with an @RestController that returns JSON.
Checked against real execution · Checked by sending 16 kinds of requests to a Spring Boot 4.1.1 server
Concepts in this lab
- MVC (Model-View-Controller)
- A software design pattern that divides a program into three roles: model, view, and controller. The model manages the data and the logic that works on it, the view handles how the data is displayed, and the controller takes the incoming input and updates the model and the view. Spring MVC is a web framework that follows this pattern. DispatcherServlet receives every request and calls the matching controller method; the controller puts the data to be shown into the model and returns it together with a view name, and the view renders the response from that data. In a REST API that responds with JSON, an HttpMessageConverter writes the return value of the controller to the response body instead of a view rendering a page.
- 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.
- HandlerMapping and HandlerAdapter
- These are components that DispatcherServlet uses when it processes a request. HandlerMapping finds the handler (controller method) that matches the request URL and method. HandlerAdapter actually runs the handler that was found: it converts the request data to fit the method's parameters, passes it in, and processes the return value.
- HandlerMethodArgumentResolver
- This component takes a value out of the request and builds it so that it can be passed to a parameter of a controller method. Parameters annotated with @RequestParam, @PathVariable, @RequestBody, and so on are filled in through it. If you implement your own, a controller can receive a recurring value, such as the logged-in user's information, directly as a parameter.
- HttpMessageConverter
- This component converts an HTTP request body to a Java object, and a Java object to an HTTP response body. @RequestBody and @ResponseBody use it. For JSON, a converter that uses the Jackson library does the conversion.
- Servlet filter
- This component intercepts and processes requests and responses at the servlet container level, and it is part of the Servlet standard. It acts before a request reaches DispatcherServlet and on the way back of the response, and it is used for work that spans all requests, such as setting the encoding, handling CORS, and authentication. Spring Security also works as a group of these components.
- Interceptor (HandlerInterceptor)
- This component runs before and after controller execution, inside Spring MVC. It can step in at three points: before the controller runs (preHandle), after it runs (postHandle), and after request processing is complete (afterCompletion). It can also know which controller method will run. A filter is a feature of the servlet container, while this component is a feature of Spring.
- @ControllerAdvice and @ExceptionHandler
- This feature lets you handle the exceptions raised in several controllers in one place. If you put an @ExceptionHandler method for each kind of exception inside an @ControllerAdvice class, an exception raised while a controller runs is passed to that method, which can build an error response in a consistent format.
- ViewResolver
- This component turns the view name (a string) returned by a controller into the view object that actually renders the page. It is used when the server builds HTML and sends it as the response, and it is not used in a REST API that responds with JSON directly.
Try these
- Default settings — The full path of a request that returns a page and is handled normally. The controller, the model, and the view all appear.
- A controller that returns JSON — The returned object becomes the response body directly, without a model or a view.
- Exception in the controller — An @ExceptionHandler produces the response. postHandle is skipped.
- With nothing to handle the exception — The exception propagates up to the servlet container and becomes a 500 response.
- Rejected by a filter — The request does not even reach DispatcherServlet. @ControllerAdvice cannot take part either.
- Stopped by the interceptor — The request goes back without the controller being called.
Running with the default settings
- GET /orders arrives at the servlet container (Tomcat) and enters the filter chain. Filters work outside Spring MVC, at the servlet level. The encoding, authentication, and logging filters each call chain.doFilter in turn to pass the request on.
- DispatcherServlet receives the request. Every request comes in through this one servlet, which sends each request to the right controller (the front controller pattern).
- HandlerMapping looks for the handler for GET /orders. It returns the list method annotated with @GetMapping("/orders") together with the list of interceptors that apply to this path.
- A HandlerAdapter is chosen. RequestMappingHandlerAdapter, which knows how to run annotated controller methods, is selected.
- The preHandle method of the interceptor runs and returns true. Unlike a filter, an interceptor works knowing which controller method is going to run.
- HandlerMethodArgumentResolver prepares the arguments for the parameters of the controller method. For the parameter of type Model, it passes in an empty Model object that will hold the data for the view. The data put into it is the model (M) of MVC.
- The controller method runs. This is the step where the controller (C) of MVC does its work. It calls the service layer to get the list of orders, puts the list into the model under the name orders, and returns the view name "orders/list".
- The return value is handled. The string "orders/list" returned by the @Controller is interpreted as a view name and is bundled with the model into a ModelAndView.
- The postHandle method of the interceptor is called. The view has not been rendered yet, so common data can still be added to the model.
- ViewResolver resolves the view name "orders/list" to an actual template file, and the view renders the HTML with the model data and writes it to the response. This is the step where the view (V) of MVC does its work, and it reads the orders that the controller put into the model.
- The afterCompletion method of the interceptor is called. This is after the response has been written, and it is called whether or not an exception was thrown.
- The response goes out to the client back through the filter chain. Filter code written after the chain.doFilter call runs at this point.
Result of this run
- The controller handles the request, puts the data into the model, and returns only a view name. The view receives that model and renders the HTML. DispatcherServlet is what connects the two.
Code
@Controller
class OrderController {
@GetMapping("/orders")
String list(Model model) {
model.addAttribute("orders", orderService.findAll());
return "orders/list"; // view name
}
}
Assumptions of this model
The lab shows a simple setup with one interceptor and one filter. For all 16 combinations, HTTP requests were sent to a real Spring Boot 4.1.1 server (embedded Tomcat), and the response code, whether the controller, postHandle, and afterCompletion ran, and what produced the response were confirmed to match the screen (the view response was checked by rendering with a view object of the same name instead of a template engine). That an unhandled exception is forwarded again to /error and becomes a 500 response is the default error handling behavior of Spring Boot, and that forwarding is shown as a single step. The case where no controller is mapped to the request (404) is not included, because the path differs depending on the version and the static resource configuration.
Previous lab: Servlets · Next lab: Request mapping and parameter binding