Front Controller (MVC)
The front controller pattern funnels every incoming request through a single handler that centralises shared concerns — routing, logging, security, view selection — before delegating to the code that does the real work. In Spring MVC that single entry point is the DispatcherServlet. Understanding its request-handling flow demystifies how a plain @RestController method ends up serving an HTTP request.
Why a front controller
Without a front controller, each endpoint would have to repeat cross-cutting plumbing: parse the request, decide which handler matches, convert input and output, handle exceptions. The front controller does all of that once, in one place, then dispatches to the appropriate handler. The benefits are consistency (every request is processed uniformly) and a single seam for global behavior (interceptors, exception resolvers, content negotiation).
Spring Boot auto-configures one DispatcherServlet mapped to / — you never register it yourself; adding spring-boot-starter-web is enough.
The request-handling flow
When a request arrives, the DispatcherServlet orchestrates a fixed pipeline of collaborators:
HTTP request
│
▼
┌──────────────────────┐
│ DispatcherServlet │ ◀── the Front Controller
└──────────────────────┘
│
1. HandlerMapping: which handler matches this URL+method?
│
▼
2. HandlerAdapter: invoke the matched handler
│
┌─────────────────────┼──────────────────────────┐
│ (pre) HandlerInterceptor.preHandle() │
│ ▼ │
│ 3. Controller method (@GetMapping ...) │
│ returns object / ResponseEntity │
│ (post) HandlerInterceptor.postHandle() │
└─────────────────────┼──────────────────────────┘
│
4a. @RestController ──▶ HttpMessageConverter (object → JSON)
4b. @Controller ─────▶ ViewResolver (name → template)
│
▼
HTTP response
Each numbered step is itself a pattern: HandlerMapping and HandlerAdapter are strategy/adapter beans the dispatcher selects between, and HandlerInterceptor is a chain of decorators wrapping the call.
The collaborators
| Component | Role | Pattern |
|---|---|---|
DispatcherServlet | Single entry point, orchestrator | Front Controller |
HandlerMapping | Maps URL + method → handler | Strategy |
HandlerAdapter | Invokes the handler uniformly | Adapter |
HandlerInterceptor | Pre/post hooks around handling | Decorator |
HttpMessageConverter | Serialises body to/from JSON, etc. | Strategy |
ViewResolver | Resolves a view name to a template | Strategy |
HandlerExceptionResolver | Turns exceptions into responses | Strategy |
For an annotation-based app, the relevant HandlerMapping is RequestMappingHandlerMapping (it reads @RequestMapping/@GetMapping etc.) and the adapter is RequestMappingHandlerAdapter.
How it relates to @RestController
Your controller is the handler the dispatcher ultimately calls. You write only the business step; the front controller supplies everything around it.
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping("/{id}")
public ProductDto get(@PathVariable Long id) {
return new ProductDto(id, "Keyboard", new BigDecimal("49.90"));
}
}
For GET /api/products/42 the dispatcher: matches this method via RequestMappingHandlerMapping, resolves 42 into the id parameter (via a HandlerMethodArgumentResolver — see the strategy pattern), invokes the method, and — because @RestController implies @ResponseBody — runs the returned ProductDto through Jackson’s HttpMessageConverter.
Output:
{ "id": 42, "name": "Keyboard", "price": 49.90 }
The @RestController is the difference between path 4a and 4b in the diagram: it short-circuits view resolution and writes the return value straight to the response body as JSON. A plain @Controller returning a String would instead go through a ViewResolver to render a template. See REST controllers.
Note: Global concerns plug into the front controller, not into individual controllers. A
@ControllerAdviceregisters an exception resolver for all controllers, and aHandlerInterceptor(or servletFilter) wraps every request — both made possible by routing everything through the singleDispatcherServlet.
Tip: To see the flow live, set
logging.level.org.springframework.web=DEBUG. Spring logs the matched handler method, the chosen argument resolvers, and the selected message converter for each request.