How does spring MVC / spring boot convert request and response data

How does spring MVC / spring boot convert request and response data

1. Problem description

When using spring MVC and spring boot for daily restful style development, they often interact through json data. How can the json data passed in from the front end be parsed into Java objects as API input parameters, and how can the API return results parse Java objects into json format data and return them to the front end?

2. Analysis

These two annotations should be frequently used in development: @ RequestBody and @ ResponseBody. In the whole process of data flow, the org.springframework.http.converter.HttpMessageConverter interface is the focus.
The HttpMessageConverter interface has the following five methods:

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, @Nullable MediaType var2);

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

1) Before the front end initiates a request and calls the background method, it will select the appropriate HttpMessageConverter implementation class according to the @ RequestBody annotation to parse the request parameters into variables. Its canRead() method returns true, and then its read() method will read the request parameters from the request and bind them to the method variables.

2) After the backend executes the method, because the return value identifies @ ResponseBody, springmvc/springboot will use the write() method of the HttpMessageConverter implementation class to write the result value into the response message. Of course, the canWrite() method returns true at this time.
The flow diagram is as follows:

In the process of source code analysis, a request message and a response message are abstracted into a request message HttpInputMessage and a response message HttpOutputMessage respectively.

3. Class diagram


MappingJackson2HttpMessageConverter: it is responsible for reading and writing json format data;
ByteArrayHttpMessageConverter: it is responsible for reading data in binary format and writing data in binary format;
StringHttpMessageConverter: it is responsible for reading data in string format and writing data in binary format;
ResourceHttpMessageConverter: responsible for reading resource files and writing out resource file data;
FormHttpMessageConverter: responsible for reading the data submitted by the form (the data format that can be read is application/x-www-form-urlencoded, and the data in multipart / form data format cannot be read);
Be responsible for writing data in application/x-www-from-urlencoded and multipart / form data formats;
Jaxb2root elementhttpmessageconverter: it is responsible for reading and writing data in xml tag format;
AtomFeedHttpMessageConverter: responsible for reading and writing data in Atom format;
RssChannelHttpMessageConverter: responsible for reading and writing data in RSS format;

4.WebMvcConfigurationSupport class

In this class, there is a method getMessageConverters to obtain the configured MessageConverter. If it is not configured, addDefaultHttpMessageConverters will be called

    protected final List<HttpMessageConverter<?>> getMessageConverters() {
        if (this.messageConverters == null) {
            this.messageConverters = new ArrayList();
            this.configureMessageConverters(this.messageConverters);
            if (this.messageConverters.isEmpty()) {
                this.addDefaultHttpMessageConverters(this.messageConverters);
            }

            this.extendMessageConverters(this.messageConverters);
        }

        return this.messageConverters;
    }

5. Data flow

The data request and response must go through the doDispatch method of the DispatcherServlet class:

   protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

                try {
                    processedRequest = this.checkMultipart(request);
                    multipartRequestParsed = processedRequest != request;
                    mappedHandler = this.getHandler(processedRequest);
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }
                    // The Adapter here is actually a RequestMappingHandlerAdapter
                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
                    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;
                    }
                   // Actual handler
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new NestedServletException("Handler dispatch failed", var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

Tags: Java Spring Boot RESTful

Posted on Wed, 27 Oct 2021 04:39:26 -0400 by herrin