前言

  • 不知道你还记不记得我们当初在学习servlet的时候有句口号叫:【一杯茶一根烟,一个参数我传一天】
  • 是的,servlet的传参是真的复杂,在业务开始之前我们得将参数进行校验、格式化赋值等操作才能做业务开发。但是自从用了spring我们再也不愁了。更确切<typo id="typo-133" data-origin="的" ignoretag="true">的</typo>说自从用了springboot我们被解放了。

HttpMessageConverter

消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

  • 在web开发中浏览器就是客户端、我们java程序放在tomcat等容器中就是服务端。客户端和服务端之间的交互时通过IO流的方式进行交互的。
  • 客户端需要保存数据,提交给服务端,服务端持久化到数据库然后再告诉客户端保存成功!这个过程就涉及服务端和客户端的两次交涉
  • 第一次客户端将保存的数据通过字节流的方式提交给服务端。保存成功后服务端将保存成功的信息再次以字节流的方式返回完成交互!!!

疑问

  • 既然交互时通过字节流,那么我们在springboot项目中可从来没写过转字节流的动作。都是直接返回Java基本类型对象或者包装对象
    消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

  • 既然我们没有做,那么springboot肯定帮我们做了。这就迁出今天的主角了。SpringMvc 中的HttpMessageConverter帮我们解决数据格式转换的问题了。
/**
 * Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
 * 用于实现request到response之间数据互转的接口
 */
public interface HttpMessageConverter<T> {}

请求入口

  • RequestMappingHandlerAdapter是针对我们controller层中@RequestMapping注解的方法。关于springmvc的执行流程我们之前也有梳理过。RequestMappingHandlerAdapter是mvc中HandlerAdapter最经典的一类。他所承载的如何通过请求定位方法且进行方法入参和出参的格式转换。
  • HandlerAdapter中有个handle方法,源码中翻译过来就是request请求入口函数。
    消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

  • 其中重点就在RequestMappingHandlerAdapter的invokeHandlerMethod上。

消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

  • 我们通过idea断点调试也能够看到controller的调用是从RequestMappingHandlerAdapter的invokeHandlerMethod上开始调用的。

消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

  • 最终会在RequestResponseBodyMethodProcessor进行数据返回的拦截。这个类对入参和出参都进行拦截。我这里值断点了他的返回函数。对入参数据的绑定在readWithMessageConverters方法中。而在数据写回过程中是他的父类AbstractMessageConverterMethodProcessor#writeWithMessageConverters起到关键作用。该方法内部会通过注册好的converter进行匹配请求头中的contentType来找到合适的转换器。而关于这个转换器spring在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());
}
  • 这也对应上了我们mvc章节中提供的扩展
/**
 * 定制消息转换器, 控制对象序列化格式
 */
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    //converters.clear();
    converters.add(getFormHttpMessageConverter());
    converters.add(getJsonHttpMessageConverter());
}
  • 这样我们就在spring默认的转换器基础上新增了我们的转换器。这里的messageConverters就是我们今天的重点HttpMessageConverter。

作用

public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T>
  • springboot中对他进行了抽象。我们自定义的消息转换器大多继承这个抽象类就可以了。
public interface HttpMessageConverter<T> {

   /**
    * 标识指定class是否可以使用该转换器
    */
   boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

   /**
    * 该转换器是否支持将该class写出
    */
   boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

   /**
    * 该转换器支持的请求头设置的mediaType
    */
   List<MediaType> getSupportedMediaTypes();

   /**
    * 从输入字节流中读取出指定class的数据
    */
   T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
         throws IOException, HttpMessageNotReadableException;

   /**
    * 将指定class的数据输出到字节流中
    */
   void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
         throws IOException, HttpMessageNotWritableException;

}

InvocableHandlerMethod

  • 上面提到请求最终会落在RequestMappingHandlerAdapter的invokeHandlerMethod上。这个方法就是根据parameter和类对象决定使用具体哪一个HandlerMethodArgumentResolver来执行数据的解析工作。
@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;
}
  • 根据MethodParameter获取对应的处理器也是通过每个处理器内置的支持方法来验证的。比如说RequestResponseBodyMethodProcessor他的支持很简单,凡是方法上带有RequestBody注解的都支持

消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了

扩展

  • 我们可以自定义一个HttpMessageconverter,然后直接注册成bean就会被springmvc解析到然后添加到消息转换器的执行链上。也可以通过实现WebMvcConfigurer在extendMessageConverters方法中添加我们自定义的转换器。这里笔者不在实现
  • 同样在WebMvcConfigurer中还有一个addArgumentResolvers方法。通过这个方法名我们也可以知道他是支持我们上面提到的HandlerMethodArgumentResolver。他是负责解析我们参数的全过程。比如RequestParamMethodArgumentResolver负责解析RequestParam标注的请求类方法;RequestResponseBodyMethodProcessor负责解析RequestBody标注的请求类方法。前者通过addFormats中进行参数格式解析,后者通过HttpMessageconverter进行解析,正常是jackson进行解析当然他的内部也完成了数据格式的转换操作。
  • 我们平时开发中经常需要上传,下载文件。我们是否可以基于他自定义一个HandlerMethodArgumentResolver来实现下载数据时只需要准备流加上自定义注解就完成下载功能呢?