Viewing spring mvc request processing process from Dispatcher Servlet

Keywords: Java Spring Programming JSP xml

Dispatcher Servlet to see spring mvc request processing

Framework

Official Architecture Map

You can see the request processing process, and the Dispatcher Servlet plays the role of front controller.

life cycle

As you can see from the source code, Dispatcher Servlet inherits from Framework Servlet, Framework Servlet inherits HttpServlet Bean, and HttpServlet Bean inherits HttpServlet.
The three stages of the Servlet life cycle are [init-service-destroy]
So for Dispatcher Servlet, it's similar to the initial servlet programming. Inherit HttpServlet, rewrite doGet, doPost, jump to jsp page in the method, use annotations or register Servlet in xml file.

Initialization

In HttpServletBean, the init() method of the HttpServlet class is overwritten.
The preceding is to read the <init-param> configuration elements in web.xml under the Dispatcher Servlet using JavaBean (that is, through the setter method) into the Dispatcher Servlet.
It is worth mentioning that there is one sentence in it.

initServletBean();

But in HttpServletBean, an empty method is left to subclasses to implement. This is the template method, which defines the execution process in the parent class and leaves the variable part to the subclass implementation. It embodies the principle of opening and closing.

Key sentence of initServletBean in Framework Servlet

 this.webApplicationContext = initWebApplicationContext();

So the meaning of Framework Servlet's existence is also used to extract the process of creating Web Application Context context. Create a Spring container context associated with Servlet and register it in ServletContext.

Because the Dispatcher Servlet rewrites onRefresh, after setting up the context, it enters the Dispatcher Servlet class through the callback of the onRefresh (Application Context context) method.
initStrategies() in the onRefresh method encapsulates the initialization strategy
Taking detectAllHandler Mappings as an example, detectAllHandler Mappings defaults to true, registering all Handler Mapping type beans in the context in the handler Mappings List variable.

Summary: HttpServletBean completes dependency injection of <init-param> configuration elements, Framework Servlet completes the establishment of container context, Dispatcher Servlet completes the initialization strategy of Spring MVC specific programming elements.

Service

Take the Get request as an example. After the delegation of the service() method in the HttpServlet base class, the request is forwarded to the doGet() method. The doGet() method is overridden in the parent FrameworkServlet class of Dispatcher Servlet.
The process Request method is encapsulated, the key is doService(request, response);
The first part is to set the Locale objects and attributes of the current request to the ThreadLocal objects in the two abstract classes of LocaleContextHolder and RequestContextHolder respectively, that is, to bind the two things and the request thread respectively. After the doService() processing is completed, the local ContextHolder and RequestContextHolder before the request are restored, that is, the thread binding is unbound. At the end of each request processing, the container context publishes a ServletRequestHandled Event event, which you can register a listener to listen for.

Just some thread-safe isolation.

doService is also an abstract method. Subclass implementation. Implemented in Dispatcher Servlet
doDispatch(request, response);
Several calls to the requet.setAttribute() method set the previously instantiated objects in the initialization process to the attributes of the http request for further processing, including the context object of the container, the localization parser and other Spring MVC-specific programming elements.

In doDispatch

mappedHandler = getHandler(processedRequest); get the handler that handles the request and return to Handler Execution Chain
Handler adapter ha = getHandler adapter (mappedHandler. getHandler ()); get handler adapter for processing requests
mappedHandler.applyPreHandle(processedRequest, response executes interceptor's prehandle method
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); calls handler actually and returns ModelAndView
Apply DefaultViewName (processed Request, mv); Set the name of view

mappedHandler.applyPostHandle(processedRequest, response, mv); execute intercepter's postHandle method,
Process Dispatch Result (processed Request, response, mappedHandler, mv, dispatch Exception); send results

data structure

Handler Mapping, Handler Adapter, View interface design.
Handler Adapter: It's an interface. The support method determines whether the adapter supports handler instances according to its type. The handler method processes requests with a given handler.

public interface HandlerAdapter {
    boolean supports(Object handler);

    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    long getLastModified(HttpServletRequest request, Object handler);
}

In the Handler Mapping interface:
getHandler gets the handler of the request and all interceptors, and returns the object of Handler ExecutionChain

public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    //...
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

View interface:
Mainly render method
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

Handler Execution Chain: Mainly includes a list of interceptors and a handle, where handler is referenced by an Object object and no interface is bound. This shows that any object can be used as a final processing object to generate views.
ModelAndView is the result of processing, mainly including the view referenced by Object and the model referenced by ModelMap. View can be a view name (String) or an instance of view. ModelMap inherits LinkedHashMap, which is a map with attribute names and values.

Handler Interceptor, which defines the implementation of interceptors
preHandle, postHandle, afterCompletion, just like in doDispatch, surrounds the hanlder implementation, executed before handler execution, after execution and after rendering, respectively.

public interface HandlerInterceptor {

    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;
}

Deep Processing Process

mappedHandler

mappedHandler = getHandler(processedRequest);
It can be seen that getHandler The method is to traverse what was already obtained at initialization handlerMappings,If you find one HandlerMapping,getHandler Method returns no null,So that means we found this one. mappedHandler,And return.

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace(
                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

The implementation of getHandler is in the AbstractHandler Mapping class. Handler and Interceptor are found according to request, combined into Handler ExecutionChain type and returned.

@Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        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 = getApplicationContext().getBean(handlerName);
        }

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

GetHandler International is an interface
AbstractHandler Method Mapping and AbstractUrlHandler Mapping all implement it. AbstractHandler Method Mapping is more commonly used. The way to annotate @RequestMapping belongs to AbstractHandler Method, which is annotated as handler.

protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

lookupHandlerMethod Method for Finding Urls and Corresponding Methods

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);

    this.mappingRegistry.acquireReadLock();
    try {
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

Get the mapping of the matching path from the mapping Registry and sort to get the most matching handler method

    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<Match>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
            Collections.sort(matches, comparator);
    
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                            request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
                }
            }
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
getHandlerAdapter

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

It also traverses all adapters in handler adapters and returns handler adapter if it matches the type of handler.

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        for (HandlerAdapter ha : this.handlerAdapters) {
            if (ha.supports(handler)) {
                return ha;
            }
        }
        throw new ServletException("No adapter for handler [" + handler +
                "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    }
applyPreHandle

mappedHandler.applyPreHandle(processedRequest, responseExecute sequentially interceptor Of prehandle Method, if another interceptor returns false Stop

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
handle

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

This method has several implementations in the handler adapter interface, AbstractHandler Method Adapter, Annotation Method Handler Adapter,,,
SimpleServletHandlerAdapter: The handle method calls the service((Servlet) handler).service(request, response);.
SimpleController Handler Adapter: The handle method is essentially to execute the Controller.handleRequest method return ((Controller) handler).handleRequest(request, response);
HttpRequestHandlerAdapter: ((HttpRequestHandler) handler).handleRequest(request, response);
AbstractHandler Method Adapter: It is an abstract class. handle method calls handleInternal. HandleInternal is an interface, which is implemented in the implementation class RequestMapping Handler Adapter. The key is to call invokeHandler Method

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    ModelAndView mav;
    checkRequest(request);
//...
    mav = invokeHandlerMethod(request, response, handlerMethod);
//..
    prepareResponse(response);
//..
    return mav;
}

invokeHandlerMethod is executing the incoming handler method

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,  
    HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {  
    .........  
    //Method of executing RequestMapping annotations in Controller  
    invocableMethod.invokeAndHandle(webRequest, mavContainer);  
      
    //Return to the Model AndView view  
    return getModelAndView(mavContainer, modelFactory, webRequest);  
}  
applyDefaultViewName

applyDefaultViewName(processedRequest, mv);
It's very simple. Just set it up. view

private void applyDefaultViewName(HttpServletRequest request, ModelAndView mv) throws Exception {
    if (mv != null && !mv.hasView()) {
        mv.setViewName(getDefaultViewName(request));
    }
}
applyPostHandle

Ibid. applyPreHandle, execute the postHandle method in the interceptor list

processDispatchResult

The key is to call the render method and then execute the AfterCompletion method in the interceptor list

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
        boolean errorView = false;
        if (exception != null) {
            if (exception instanceof ModelAndViewDefiningException) {
                logger.debug("ModelAndViewDefiningException encountered", exception);
                mv = ((ModelAndViewDefiningException) exception).getModelAndView();
            }
            else {
                Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                mv = processHandlerException(request, response, handler, exception);
                errorView = (mv != null);
            }
        }

        // Did the handler return a view to render?
        if (mv != null && !mv.wasCleared()) {
            render(mv, request, response);
        }
        if (mappedHandler != null) {
            mappedHandler.triggerAfterCompletion(request, response, null);
        }
    }

In render method, the key step is view. render (mv. getModel Internal (), request, response);
This interface defines template methods in AbstractView, an abstract class

@Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {


        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        prepareResponse(request, response);
        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }

createMergedOutputModel, the key is these putAll methods, put static attributes and dynamic values into mergedModel and return, you can see put static attributes first and then put model, so it shows that dynamic value priority may override static attributes.

protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,
        HttpServletResponse response) {
        ...
    Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
    mergedModel.putAll(this.staticAttributes);
    if (pathVars != null) {
        mergedModel.putAll(pathVars);
    }
    if (model != null) {
        mergedModel.putAll(model);
    }
    // Expose RequestContext?
    if (this.requestContextAttribute != null) {
        mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));
    }
    return mergedModel;
}

prepareResponse is to set the response header

protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
        if (generatesDownloadContent()) {
            response.setHeader("Pragma", "private");
            response.setHeader("Cache-Control", "private, must-revalidate");
        }
    }

renderMergedOutputModel is another interface
protected abstract void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

There are many implementations, for jsp, in the InternalResourceView class

@Override
    protected void renderMergedOutputModel(    Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Expose the model object as request attributes.
        exposeModelAsRequestAttributes(model, request);
        // Expose helpers as request attributes, if any.
        exposeHelpers(request);

        // Determine the path for the request dispatcher.
        String dispatcherPath = prepareForRendering(request, response);

        // Obtain a RequestDispatcher for the target resource (typically a JSP).
        RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);

        // If already included or response already committed, perform include, else forward.
        if (useInclude(request, response)) {
            response.setContentType(getContentType());
            rd.include(request, response);
        }

        else {
            // Note: The forwarded resource is supposed to determine the content type itself.
            rd.forward(request, response);
        }
    }

The exposeModelAsRequestAttributes method is to fill in the request with all the values in the model.

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
    for (Map.Entry<String, Object> entry : model.entrySet()) {
        String modelName = entry.getKey();
        Object modelValue = entry.getValue();
        if (modelValue != null) {
            request.setAttribute(modelName, modelValue);
        }
        else {
            request.removeAttribute(modelName);
        }
    }
}

If response has been submitted, include, otherwise forward is executed
Here, the request processing ends.

Posted by rajbal on Sat, 06 Apr 2019 21:12:32 -0700