SpringMVC request processing adapter and processor source code analysis

What does SpringMVC do for you when an HTTP request comes in?

Last SpringMVC source analysis because of the length of the reason will only request mapper source process analysis completed, I do not know what you think of the last process analysis? Free time is not their own after a source!

First above:

Last article, I did a very detailed process analysis of the Handler processor mapper, so this article will be around the processor adapter, processor two processes to analyze the source!

1. Source code analysis of processor adapter

In fact, the last article also explained the general logic behind, but in fact, SpringMVC as an excellent framework, it is very comprehensive, in fact, there is not only one way to develop a Controller plus @Controller, there is also interface based implementation, How does SpringMVC feel about different processing methods, such as implementing the Colltroller interface and implementing the HttpRequestHandler interface? So, after SpringMVC finds the corresponding mapping method based on the request path, how can it tell if the method was created in one of the three ways? This is where the processor adapter comes in! Look at a piece of code!

// Get the details of the mapping method according to the request path
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
}

// Call the processor adapter to find the corresponding processor for this method
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
Copy the code

Let’s take a look at the logic inside the processor adapter

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters ! =null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                returnadapter; }}}throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
Copy the code
  • First he loops a loop calledhandlerAdaptersWhere is this property set? There is one in the spring-webmVC.jar directory calledDisPatcherServlet.properties“, internally defines three processors. Why three processors? As stated above, there are three ways to code controllers, so there are three corresponding processors!

  • This method loops through all adapter schemes until the appropriate processor returns, or else is thrownServletExceptionAbnormal!

2. Processor source code parsing

When the handler returns, the next step is to take the handler and process our corresponding method! How do you deal with that? We went back to the original org. Springframework. Web. Servlet. DispatcherServlet# doDispatch method

We enter to code logic processor internal org. Springframework. Web. Servlet. MVC) method. The AbstractHandlerMethodAdapter# handle

Org. Springframework. Web. Servlet. MVC) method. The annotation. RequestMappingHandlerAdapter# handleInternal method (note here, we are in common Analysis by @controller registration)

So let’s look at some code

// Check whether the synchronization block needs to be moved (usually not set to synchronization)
// Sessions are not thread-safe. To ensure that the same session is accessed correctly across multiple requests, set synchronizeOnSession to TRUE.
if (this.synchronizeOnSession) {
    HttpSession session = request.getSession(false);
    if(session ! =null) {
        Object mutex = WebUtils.getSessionMutex(session);
        synchronized(mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); }}else {
        // No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod); }}else {
    // Let's click inside to see the internal implementation of this method
    mav = invokeHandlerMethod(request, response, handlerMethod);
}
Copy the code

invocableMethod.invokeAndHandle(webRequest, mavContainer); Method internal implementation!

Org. Springframework. Web. Method. Support. InvocableHandlerMethod# invokeForReques method implementation

  • We can see that there is a logical code like this
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {

    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    return doInvoke(args);
}
Copy the code
  • getMethodArgumentValuesGets the parameters of the method, and the values passed
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
	// Get method argument list (parameter list)
    MethodParameter[] parameters = getMethodParameters();
    if (ObjectUtils.isEmpty(parameters)) {
        return EMPTY_ARGS;
    }
	// Build an array of parameter objects
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
        // Get the parameter object
        MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs);
        if(args[i] ! =null) {
            continue;
        }
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        try {
        // This method is used to obtain the corresponding parameters in the request object from the parameter name of the method and assign them to the corresponding parameter object
            args[i] = this.resolvers.resolveArgument(parameter, mavContainer, 
                                                     request, this.dataBinderFactory);
        }
        catch (Exception ex) {
 
            if (logger.isDebugEnabled()) {
                String exMsg = ex.getMessage();
                if(exMsg ! =null&&! exMsg.contains(parameter.getExecutable() .toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); }}throwex; }}// Returns all parameters and their values
    return args;
}
Copy the code

doInvoke(Object… args)

  • The processor starts executing the method reflectively, and the main logic of the execution is as follows:
@Nullable
protected Object doInvoke(Object... args) throws Exception {
    ReflectionUtils.makeAccessible(getBridgedMethod());
    try {
        returngetBridgedMethod().invoke(getBean(), args); }... Ignore.. }Copy the code

Get the method object, get the object instance from the Bean factory, pass the parameter to set the method, and get the method return value!

After get the return value, step by step back, back to the org. Springframework. Web. Servlet. MVC) method. The annotation. ServletInvocableHandlerMethod# invokeAndHandle method

Finally the method is invoked org. Springframework. Web. Servlet. MVC) method. The annotation. RequestResponseBodyMethodProcessor# handleReturnValue

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
    throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

    mavContainer.setRequestHandled(true);
    ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
    ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

    // Try even with null return value. ResponseBodyAdvice could get involved.
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
Copy the code

Finally through writeWithMessageConverters (returnValue, returnType inputMessage, outputMessage); Write the execution results to the page (actually Servlet development is the same logic, but SpringMvc layer encapsulation optimization)!

It is not hard to see SpringMvc intercepting request to processing request mapping method, although I am not finished, but can summarize a little:


If the understanding of the article is wrong, welcome big men private chat correction! Welcome to pay attention to the author’s public number, progress together, learn together!