Deep Dive into SpringMVC DispatcherServlet Request Processing
Core DispatcherServlet Processing Method
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest currentRequest = request;
HandlerExecutionChain handlerChain = null;
boolean isMultipart = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView viewInstance = null;
Exception caughtException = null;
try {
// Handle multipart requests if file upload is detected
// Uses MultipartResolver component internally
currentRequest = checkMultipart(request);
isMultipart = (currentRequest != request);
// Locate handler for incoming request
// Iterates through registered HandlerMapping instances
handlerChain = getHandler(currentRequest);
if (handlerChain == null) {
noHandlerFound(currentRequest, response);
return;
}
// Select appropriate adapter for the handler type
// Adapters handle invocation differences between handler types
HandlerAdapter adapterInstance = getHandlerAdapter(handlerChain.getHandler());
// Handle caching headers for GET/HEAD requests
String httpMethod = request.getMethod();
boolean isReadOperation = HttpMethod.GET.matches(httpMethod);
if (isReadOperation || HttpMethod.HEAD.matches(httpMethod)) {
long lastModifiedTime = adapterInstance.getLastModified(request, handlerChain.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModifiedTime) && isReadOperation) {
return;
}
}
// Trigger interceptor preHandle callbacks in sequence
// If any interceptor returns false, trigger afterCompletion and exit
if (!handlerChain.applyPreHandle(currentRequest, response)) {
return;
}
// Execute the actual handler via adapter
viewInstance = adapterInstance.handle(currentRequest, response, handlerChain.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// Set default view name if not specified
// Uses RequestToViewNameTranslator component
applyDefaultViewName(currentRequest, viewInstance);
// Trigger interceptor postHandle callbacks in reverse order
handlerChain.applyPostHandle(currentRequest, response, viewInstance);
}
catch (Exception ex) {
caughtException = ex;
}
catch (Throwable err) {
caughtException = new ServletException("Handler dispatch failed: " + err, err);
}
// Process final result including exception handling and view rendering
processDispatchResult(currentRequest, response, handlerChain, viewInstance, caughtException);
}
catch (Exception ex) {
triggerAfterCompletion(currentRequest, response, handlerChain, ex);
}
catch (Throwable err) {
triggerAfterCompletion(currentRequest, response, handlerChain,
new ServletException("Handler processing failed: " + err, err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (handlerChain != null) {
handlerChain.applyAfterConcurrentHandlingStarted(currentRequest, response);
}
}
else {
if (isMultipart) {
cleanupMultipart(currentRequest);
}
}
}
}
Handler Mapping Resolution
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain executionChain = mapping.getHandler(request);
if (executionChain != null) {
return executionChain;
}
}
}
return null;
}
The framework maintains multiple HandlerMapping implementations, with RequestMappingHandlerMapping handling @Controller annotated components and BeanNameUrlHandlerMapping managing bean-name based routing. The returned HandlerExecutionChain encapsulates both the handler method and associated interceptors.
Adapter Selection Strategy
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
Since SpringMVC supports diverse handler types without a common interface, the adapter pattern enables flexible handler invocation. The framework includes four distinct adapter implementations to accommodate various handler strategies.
Execution Flow Summary
- Multipart request detection and preprocessing
- Handler lookup through HandlerMapping chain
- Adapter selection based on handler type
- Conditional request caching validation for GET/HEAD
- Sequential interceptor preHandle execution
- Adapter-based handler invocation returning ModelAndView
- Default view name resolution if needed
- Reverse-order interceptor postHandle execution
- Result processing encompassing exception handling and view rendering
- AfterCompletion callbacks for cleanup and resource release