Message serialization spring has done so much for us that I almost can't serialize it myself

preface

  • I don't know if you remember the slogan when we were learning servlet: "a cup of tea, a cigarette, a parameter, I'll pass it on for a day"
  • Yes, the parameter transfer of servlet is really complex. Before the business starts, we have to verify, format and assign parameters for business development. But since we used spring, we don't worry anymore. More specifically, we have been liberated since using springboot.

HttpMessageConverter

  • In web development, the browser is the client, and our java programs in containers such as tomcat are the server. The interaction between the client and the server is carried out through IO flow.
  • The client needs to save the data and submit it to the server. The server will persist it to the database, and then tell the client that the save is successful! This process involves two negotiations between the server and the client
  • For the first time, the client submits the saved data to the server through byte stream. After saving successfully, the server will return the saved information in the form of byte stream again to complete the interaction!!!

doubt

  • Since the interaction is through byte stream, we have never written the action of converting byte stream in the spring boot project. Both directly return Java basic type objects or wrapper objects

  • Since we didn't do it, spring boot must have done it for us. That's moving out of today's protagonist. HttpMessageConverter in spring MVC helps us solve the problem of data format conversion.

/**
 * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
 * Interface used to realize data exchange between request and response
 */
public interface HttpMessageConverter<T> {}

Request entry

  • RequestMappingHandlerAdapter is a method for the @ RequestMapping annotation in our controller layer. We have combed the execution process of spring mvc before. RequestMappingHandlerAdapter is the most classic type of HandlerAdapter in mvc. It carries how to locate the method by request and convert the format of the method input and output parameters.

  • There is a handle method in HandlerAdapter, which is translated from the source code as the request entry function.

  • The focus is on the invokeHandlerMethod of the RequestMappingHandlerAdapter.

  • Through the idea breakpoint debugging, we can also see that the controller is called from the invokeHandlerMethod of the RequestMappingHandlerAdapter.

  • Finally, the data return will be intercepted in the RequestResponseBodyMethodProcessor. This class intercepts both incoming and outgoing parameters. I have his return function here. The binding of the input parameter data is in the readWithMessageConverters method. In the process of data writeback, its parent class AbstractMessageConverterMethodProcessor#writeWithMessageConverters plays a key role. In this method, the registered converter will match the contentType in the request header to find the appropriate converter. For this converter, spring defaults in the requestMappingHandlerAdapter
public RequestMappingHandlerAdapter() {
   this.messageConverters = new ArrayList<>(4);
   this.messageConverters.add(new ByteArrayHttpMessageConverter());
   this.messageConverters.add(new StringHttpMessageConverter());
   try {
      this.messageConverters.add(new SourceHttpMessageConverter<>());
   }
   catch (Error err) {
      // Ignore when no TransformerFactory implementation is available
   }
   this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
  • This also corresponds to the extensions provided in our mvc chapter
/**
 * Custom message converter to control object serialization format
 */
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    //converters.clear();
    converters.add(getFormHttpMessageConverter());
    converters.add(getJsonHttpMessageConverter());
}
  • In this way, we have added our converter based on the spring default converter. The message converters here are our focus today, httpmessage converter.

effect

public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T>
  • He is abstracted in spring boot. Most of our custom message converters inherit this abstract class.
public interface HttpMessageConverter<T> {

   /**
    * Identifies whether the specified class can use the converter
    */
   boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

   /**
    * Does the converter support writing the class out
    */
   boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

   /**
    * The mediaType of the request header setting supported by the converter
    */
   List<MediaType> getSupportedMediaTypes();

   /**
    * Read the data of the specified class from the input byte stream
    */
   T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;

   /**
    * Outputs the data of the specified class to the byte stream
    */
   void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;

}

InvocableHandlerMethod

  • As mentioned above, the request will eventually fall on the invokeHandlerMethod of the RequestMappingHandlerAdapter. This method determines which HandlerMethodArgumentResolver to use according to the parameter and class object to perform data parsing.
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
   HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
   if (result == null) {
      for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
         if (resolver.supportsParameter(parameter)) {
            result = resolver;
            this.argumentResolverCache.put(parameter, result);
            break;
         }
      }
   }
   return result;
}
  • Obtaining the corresponding processor according to the MethodParameter is also verified through the built-in support methods of each processor. For example, the RequestResponseBodyMethodProcessor supports very simple methods with the RequestBody annotation

extend

  • We can customize an HttpMessageconverter, and then directly register it as a bean, which will be parsed by spring MVC and added to the execution chain of the message converter. We can also add our custom converter in the extendMessageConverters method by implementing WebMvcConfigurer. Here the author is not in the realization
  • There is also an addArgumentResolvers method in WebMvcConfigurer. Through this method name, we can also know that it supports the HandlerMethodArgumentResolver mentioned above. He is responsible for the whole process of parsing our parameters. For example, RequestParamMethodArgumentResolver is responsible for resolving the request class methods marked by RequestParam; The RequestResponseBodyMethodProcessor is responsible for resolving the request class methods marked by the RequestBody. The former parses the parameter format through addFormats, and the latter parses through HttpMessageconverter. Normally, jackson parses. Of course, his internal has also completed the conversion of data format.
  • We usually need to upload and download files during development. Can we customize a HandlerMethodArgumentResolver based on it to complete the download function only by preparing the stream and adding custom annotations when downloading data?

Author: zxhtom
Link: https://juejin.cn/post/7023545265613176839

Keywords: Java Spring Back-end

Added by powerpants on Wed, 27 Oct 2021 10:48:22 +0300