The Spring MVC Request Flow
The stages an HTTP request passes through, from DispatcherServlet to handler mapping, adapters, and view resolvers, until it becomes a response.
Contents
Every Spring MVC request funnels into a single DispatcherServlet and then follows a fixed sequence of stages to become a response.
A single entry point: the front controller
Spring MVC works as a front controller. In that pattern, one object receives every request, finishes the shared handling, and then delegates to the appropriate controller. That entry point object is the DispatcherServlet.
DispatcherServlet is an ordinary servlet extending HttpServlet. Spring Boot maps it automatically to every path (/). When a request arrives, the standard servlet entry point service() is called. Its parent class FrameworkServlet overrides it, and the chain eventually reaches the core method doDispatch().
Because there is only one entry point, cross-cutting concerns such as logging, authentication, and exception translation can be handled in one place. Individual controllers only need to concentrate on their own business logic. Every remaining stage in the flow happens on top of doDispatch().
The eight stages inside doDispatch
Turning one request into a response breaks into three groups of work: find the handler, run it, and convert its result into a response.
| Stage | Component | Work |
|---|---|---|
| 1 | HandlerMapping | Look up the handler (controller) mapped to the request URL |
| 2 | HandlerAdapter | Look up an adapter capable of running that handler |
| 3 | HandlerAdapter | Execute the adapter that was found |
| 4 | Controller | The adapter invokes the actual handler |
| 5 | HandlerAdapter | Convert the return value into a ModelAndView and return it |
| 6 | ViewResolver | Run the view resolver with the view name |
| 7 | ViewResolver | Turn the logical name into a physical name and return a View |
| 8 | View | Render the view with the model |
This table describes the traditional view-rendering path. For a REST response, a message converter serializes the returned object into a body such as JSON instead of stages 6 through 8. Both paths go through stages 1 through 5 identically.
Handler mapping and handler adapters
Finding a controller and running it are separate jobs because controllers do not all take the same form. Handler mapping answers "who handles this request" and the handler adapter answers "how is that handler executed". That split lets annotation-based controllers and the older interface-based controllers coexist in the same flow.
Spring Boot registers several of each automatically, in priority order. Handler mapping uses the first one that matches, and the handler adapter uses the first adapter whose supports() returns true.
| Kind | Bean name | Handles |
|---|---|---|
| HandlerMapping | RequestMappingHandlerMapping | Annotation controllers based on @RequestMapping |
| HandlerMapping | BeanNameUrlHandlerMapping | Handlers looked up by Spring bean name |
| HandlerAdapter | RequestMappingHandlerAdapter | Annotation controllers |
| HandlerAdapter | HttpRequestHandlerAdapter | HttpRequestHandler implementations |
| HandlerAdapter | SimpleControllerHandlerAdapter | Implementations of the older Controller interface |
Nearly all production controllers are built on @Controller and @RequestMapping, so the RequestMappingHandlerMapping and RequestMappingHandlerAdapter pair is what usually runs. The remaining adapters are compatibility paths for static resources and legacy-style controllers.
Argument binding and response conversion
Two collaborators step in when the handler adapter executes a controller method. An ArgumentResolver fills the parameters and a ReturnValueHandler turns the return value into a response. Both call an HttpMessageConverter when needed to convert a body into an object and an object into a body.
Take request body handling as an example. @RequestBody is handled by RequestResponseBodyMethodProcessor, and HttpEntity by HttpEntityMethodProcessor; both are ArgumentResolvers. They call a message converter to turn a JSON body into a Java object. On the response side, the ReturnValueHandler for @ResponseBody serializes the object with the same converter.
@RestController
public class MemberController {
@PostMapping("/members")
public MemberResponse create(@RequestBody MemberRequest request) {
// request: the message converter turned the JSON body into an object
// return value: the ReturnValueHandler serializes it back into a JSON body
return new MemberResponse(request.getName());
}
}If the return value is a view name rather than an object, the flow takes the view resolver path at stage 6. The view resolver takes a logical name such as new-form and turns it into an actual file path. The InternalResourceViewResolver that Spring Boot registers automatically does this work. It completes the physical path by wrapping the name in the spring.mvc.view.prefix and spring.mvc.view.suffix settings.
spring:
mvc:
view:
prefix: /WEB-INF/views/
suffix: .jspWith that configuration, a controller returning new-form resolves to /WEB-INF/views/new-form.jsp. The controller only has to know the logical name, and the actual path convention is left to configuration. The controller code stays untouched even when the view technology changes.
Where filters and interceptors sit
There are two places to insert cross-cutting concerns. A filter sits in the servlet layer and wraps the outside of DispatcherServlet, while an interceptor sits inside Spring MVC and wraps the controller on both sides. Even when they do the same job, the information they can reach and their position differ.
A filter is standard servlet technology, so it only sees request and response. The Filter interface has a simple set of methods. Inside doFilter you must call chain.doFilter() to pass control to the next filter or the servlet; skip the call and the request ends there.
public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {}
void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
default void destroy() {}
}An interceptor is provided by Spring MVC, so it also receives which handler is being invoked and which ModelAndView is returned. HandlerInterceptor splits into three points in time. preHandle runs before the handler adapter is called, postHandle after, and afterCompletion once view rendering has finished.
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
return true;
}
default void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {}
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
Exception ex) throws Exception {}
}The invocation rules for those three methods are the heart of interceptor design. If preHandle returns false, neither the remaining interceptors nor the handler adapter is invoked and the flow ends there. That fits logic that has to block a request, such as a login check.
Behavior diverges when an exception occurs. If a controller throws, postHandle is skipped, but afterCompletion always runs and receives the exception object ex as a parameter. Shared cleanup that must happen regardless of exceptions therefore belongs in afterCompletion.
| Aspect | Filter | Interceptor |
|---|---|---|
| Provided by | Servlet container | Spring MVC |
| Position | Outside DispatcherServlet | Around the controller |
| Accessible information | request, response | Includes handler and ModelAndView |
| How to block | Do not call chain.doFilter | Return false from preHandle |
Summed up in one line, concerns outside MVC such as encoding or a security filter chain belong to filters, and processing that needs controller context belongs to interceptors. Spring Security being filter-based follows from the same reasoning.
Summary
The Spring MVC request flow starts at a single entry point, the DispatcherServlet. doDispatch() finds a controller through handler mapping, runs it through a handler adapter, and then builds the response through either a view resolver or a message converter. Argument binding and return value conversion belong to ArgumentResolver and ReturnValueHandler, while body serialization belongs to the message converter.
Cross-cutting concerns split between filters in the servlet layer and interceptors inside MVC. The three interceptor callbacks define where blocking and exception handling happen. Keeping this staging in mind makes it much easier to trace where validation, exception translation, and authentication each take place.