Spring MVC Request Processing Pipeline and Component Configuration
The architecture of Spring MVC relies on several distinct components working in sequence to process HTTP requests.
Core Components
- Front Controller (DispatcherServlet): Acts as the central hub for the entire request lifecycle. It receives incoming HTTP requests and delegates them to the appropriate components, effectively decoupling request handling process.
- Request Router (HandlerMapping): Determines the specific endpoint logic (Handler) that should process the current request based on URL patterns, configurations, or annotations.
- Endpoint Logic (Handler): The actual controller logic written by the developer. It executes the bussiness logic and prepares the model data for the response.
- Execution Bridge (HandlerAdapter): Facilitates the invocation of the Handler. By utilizing the adapter pattern, it allows the framework to execute a wide variety of handler methodologies seamlessly.
- Template Resolver (View Resolver): Transforms logical view names returned by the handler in to concrete physical templates (e.g., JSP, Thymeleaf) and constructs the View object.
- Rendering Layer (View): Represents the output format. It takes the model data and renders it into a format the client can understand, such as HTML, PDF, or JSON.
Annotated Driven Configuration\nThe RequestMapper, Execution Bridge, and Template Resolver form the core triad of Spring MVC. Using the <mvc:annotation-driven> tag in XML automatically registers RequestMappingHandlerMapping and RequestMappingHandlerAdapter, eliminating the need for manual bean declarations.
Specifically, RequestMappingHandlerMapping scans for @RequestMapping annotations to build the routing table. RequestMappingHandlerAdapter handles the actual method invocation, resolving parameters and handling return values. Combined with component scanning, this enables features like JSON serialization, validation, and exception handling out of the box.
Dispatch Execution Flow
The underlying request routing mechanism is encapsulated within the core dispatch method. Variables and structural flow have been refactored for clarity:
java protected void executeDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception { HttpServletRequest currentReq = req; HandlerExecutionChain executionChain = null; boolean isMultipart = false; WebAsyncManager asyncMgr = WebAsyncUtils.getAsyncManager(req);
try {
ModelAndView resultModel = null;
Exception processingError = null;
try {
currentReq = verifyMultipart(req);
isMultipart = currentReq != req;
executionChain = resolveHandler(currentReq);
if (executionChain == null) {
handleMissingHandler(currentReq, resp);
return;
}
HandlerAdapter adapter = findAdapter(executionChain.getHandler());
String httpVerb = req.getMethod();
boolean isGetRequest = "GET".equals(httpVerb);
if (isGetRequest || "HEAD".equals(httpVerb)) {
long resourceLastModified = adapter.getLastModified(req, executionChain.getHandler());
if ((new ServletWebRequest(req, resp)).checkNotModified(resourceLastModified) && isGetRequest) {
return;
}
}
if (!executionChain.applyPreHandle(currentReq, resp)) {
return;
}
resultModel = adapter.handle(currentReq, resp, executionChain.getHandler());
if (asyncMgr.isConcurrentHandlingStarted()) {
return;
}
resolveDefaultView(currentReq, resultModel);
executionChain.applyPostHandle(currentReq, resp, resultModel);
} catch (Exception err) {
processingError = err;
} catch (Throwable err) {
processingError = new NestedServletException("Handler execution failure", err);
}
finalizeDispatchResult(currentReq, resp, executionChain, resultModel, processingError);
} catch (Exception finalErr) {
triggerCompletionCallback(currentReq, resp, executionChain, finalErr);
} catch (Throwable finalErr) {
triggerCompletionCallback(currentReq, resp, executionChain, new NestedServletException("Final processing failure", finalErr));
} finally {
if (asyncMgr.isConcurrentHandlingStarted()) {
if (executionChain != null) {
executionChain.applyAfterConcurrentHandlingStarted(currentReq, resp);
}
} else if (isMultipart) {
clearMultipartData(currentReq);
}
}
}
private void resolveDefaultView(HttpServletRequest req, ModelAndView mv) throws Exception { if (mv != null && !mv.hasView()) { String fallbackView = generateDefaultViewName(req); if (fallbackView != null) { mv.setViewName(fallbackView); } } }
Modern Configuration Equivalent
Instead of XML configuration, the annotation-driven behavior and component scanning can be achieved programmatically using Java configuration classes:
java package com.example.app.config;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration @EnableWebMvc @ComponentScan(basePackages = "com.example.app.controllers") public class WebAppConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**")
.addResourceLocations("/assets/");
}
}