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

前言

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

HttpMessageConverter

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

疑问

  • 既然交互时通过字节流,那么我们在springboot项目中可从来没写过转字节流的动作。都是直接返回Java基本类型对象或者包装对象


  • 既然我们没有做,那么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请求入口函数。


  • 其中重点就在RequestMappingHandlerAdapter的invokeHandlerMethod上。
  • 我们通过idea断点调试也能够看到controller的调用是从RequestMappingHandlerAdapter的invokeHandlerMethod上开始调用的。
  • 最终会在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注解的都支持

扩展

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

作者:zxhtom
链接:https://juejin.cn/post/7023545265613176839

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容