Execution procedure of Spring MVC-02-DispatchServlet-2-getHandler()

Continue to analyze the execution of DispatchServlet-getHandler()

getHandler() Finds the handler corresponding to the request (that is, the processing method in the Controller)

/**
 * Process the actual dispatching to the handler.
 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
 * to find the first that supports the handler class.
 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
 * themselves to decide which methods are acceptable.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception in case of any kind of processing failure
 */
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);

            // Find the handler that corresponds to the request (that is, the processing method in the Controller)
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Find handler adapter based on processor
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Process the request and return the appropriate view
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            applyDefaultViewName(processedRequest, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
       
       /** Omit other code**/
}

The following analysis examines how the getHandler() method finds the corresponding handler for the request, involving a design pattern, the Intercept Filter pattern.

/**
 * Return the HandlerExecutionChain for this request.
 * <p>Tries all handler mappings in order.
 * @param request current HTTP request
 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
 */
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

getHandler() finds the corresponding HandlerMapping object from List <HandlerMapping> handlerMappings and creates a HandlerExecutionChain object from the HandlerMapping object.List <HandlerMapping> handlerMappings are done in the initStrategies() method at initialization (described in the initialization section of Dispatcher Servlet).

As you can see from the getHandler() method code of the Dispatcher Servlet class, the getHandler() method of the HandlerMapping class is called inside the getHandler() method.

The HandlerMapping related class diagram is shown in the figure.

The getHandler() method in Dispatcher Servlet actually calls the getHandler() method of AbstractHandler Mapping, which is implemented as follows:

/**
 * Look up a handler for the given request, falling back to the default
 * handler if no specific one is found.
 * @param request current HTTP request
 * @return the corresponding handler instance, or the default handler
 * @see #getHandlerInternal
 */
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    // Find the corresponding request Processor handler Based on the request
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

    if (logger.isTraceEnabled()) {
        logger.trace("Mapped to " + handler);
    }
    else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
        logger.debug("Mapped to " + executionChain.getHandler());
    }

    if (CorsUtils.isCorsRequest(request)) {
        CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
        CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
        CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
        executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
    }

    return executionChain;
}

In the above code, the getHandlerInternal() method finds the corresponding request handler based on the request, and if not, the getDefaultHandler() method is used to get the default handler.

The most important of the getHandler() method is the getHandlerExecutionChain () method, because the entire getHandler() method is for getting a HandlerExecutionChain object.The getHandlerExecutionChain() method is implemented as follows:

/**
 * Build a {@link HandlerExecutionChain} for the given handler, including
 * applicable interceptors.
 * <p>The default implementation builds a standard {@link HandlerExecutionChain}
 * with the given handler, the handler mapping's common interceptors, and any
 * {@link MappedInterceptor MappedInterceptors} matching to the current request URL. Interceptors
 * are added in the order they were registered. Subclasses may override this
 * in order to extend/rearrange the list of interceptors.
 * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
 * pre-built {@link HandlerExecutionChain}. This method should handle those
 * two cases explicitly, either building a new {@link HandlerExecutionChain}
 * or extending the existing chain.
 * <p>For simply adding an interceptor in a custom subclass, consider calling
 * {@code super.getHandlerExecutionChain(handler, request)} and invoking
 * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
 * @param handler the resolved handler instance (never {@code null})
 * @param request current HTTP request
 * @return the HandlerExecutionChain (never {@code null})
 * @see #getAdaptedInterceptors()
 */
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

As you can see from the getHandlerExecutionChain() method implementation, the first step is to determine if the processor acquired on request is a HandlerExecutionChain object.If so, use it directly; if not, create a HandlerExecutionChain object by requesting the processor Handler, then save multiple interceptor objects to the List <HandlerInterceptor> interceptorList property in the HandlerExecutionChain object.Therefore, the HandlerExecutionChain encapsulates the requested handler (that is, the handler in the Controller) and the associated interceptor, as described in the comments in the HandlerExecutionChain class code:

Handler execution chain, consisting of handler object and any handler interceptors.

Part of the code for the HandlerExecutionChain class is as follows:

/**
 * Handler execution chain, consisting of handler object and any handler interceptors.
 * Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
 *
 * @author Juergen Hoeller
 * @since 20.06.2003
 * @see HandlerInterceptor
 */
public class HandlerExecutionChain {

    private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);

    private final Object handler;

    @Nullable
    private HandlerInterceptor[] interceptors;

    @Nullable
    private List<HandlerInterceptor> interceptorList;


    /** Omit other code**/
}

Returns to the doDispatch() method of the Dispatcher Servlet class. After executing the getHandler() method, the resulting HandlerExecutionChain object, if empty, executes the noHandlerFound () method, returns directly after execution, and the process ends.The code for the noHandlerFound() method is as follows:

/**
 * No handler found -> set appropriate HTTP response status.
 * @param request current HTTP request
 * @param response current HTTP response
 * @throws Exception if preparing the response failed
 */
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (pageNotFoundLogger.isWarnEnabled()) {
        pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
    }
    if (this.throwExceptionIfNoHandlerFound) {
        throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
                new ServletServerHttpRequest(request).getHeaders());
    }
    else {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
}

In general, the private boolean throwExceptionIfNoHandlerFound = false property is not modified to true during development, so when the HandlerExecutionChain is empty, the NoHandlerFoundException exception is not thrown, but the sendError method of the HttpServletResponse is entered.The SC_NOT_FOUND response code may not be easily identifiable, so enter its code:.

public static final int SC_NOT_FOUND = 404;

This 404 return code may be familiar to everyone, which is a common HTTP request where no resource is found.

The Handler ExecutionChain is empty as described above, and the Handler ExecutionChain is not empty as analyzed below.

If the HandlerExecutionChain object after executing the getHandler() method is not empty, the getHandlerAdapter() method in the DispatchServlete class will be executed, and the next article will continue with the analysis.

Tags: Programming

Posted on Fri, 07 Feb 2020 00:58:32 -0500 by mona02