Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Deep Dive into SpringMVC DispatcherServlet Request Processing

Notes May 14 1

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

  1. Multipart request detection and preprocessing
  2. Handler lookup through HandlerMapping chain
  3. Adapter selection based on handler type
  4. Conditional request caching validation for GET/HEAD
  5. Sequential interceptor preHandle execution
  6. Adapter-based handler invocation returning ModelAndView
  7. Default view name resolution if needed
  8. Reverse-order interceptor postHandle execution
  9. Result processing encompassing exception handling and view rendering
  10. AfterCompletion callbacks for cleanup and resource release

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.