消息序列化spring简直为我们做的太多了,差点我都不会自己序列化了 | Spring刀法精准
前言
不知道你还记不记得我们当初在学习servlet的时候有句口号叫:【一杯茶一根烟,一个参数我传一天】
是的,servlet的传参是真的复杂,在业务开始之前我们得将参数进行校验、格式化赋值等操作才能做业务开发。但是自从用了spring我们再也不愁了。更确切的说自从用了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