根据日常业务需求微服务之间数据传输对象可能是比较复杂的对象,就以Mybatis-plus QueryWrapper为例,对象中的属性可能存在抽象类的引用,甚至是集合中的抽象类,如果使用json进行传输时,json可能无法转化为对象
以QueryWrapper为例,normal变量中3个元素的真是类型是不相同的,但他们都实现了接口ISqlSegment
所以当我们反序列化json字符串成对象的时候,无法知道数组中的三个ISqlSegment对象具体的实现具体是什么,所以会抛出转化异常
可以看出 json序列化的方式并没有办法在反序列化时知道到抽象类实例的具体子类型,所以博主采用了jdk的序列化的方式传输(借鉴rabbitMq SimpleMessageConverter 消息传输的思路)
这里引用一个博主的文章代码,本文以feign为例
Feign完美解决服务之间传递文件、传递list,map、对象等情况 - sprouting的个人空间 - OSCHINA - 中文开源技术交流社区
调用端代码(请求方)在编码对象的时候我们判断如果需要使用jdk编码传输,则使用jdk编码传输,否则默认使用json(上面文章是使用json编码)
package cn.ssq.config;
import cn.ssq.http.model.JdkSerializable;
import cn.ssq.uploadPolicyService.entity.OnlineShop;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import feign.RequestTemplate;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.linkedMultiValueMap;
import org.springframework.util.SerializationUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class FeignSpringFormEncoder implements Encoder{
private final List> converters = new RestTemplate().getMessageConverters();
public static final Charset UTF_8 = Charset.forName("UTF-8");
public FeignSpringFormEncoder() {}
private class HttpOutputMessageImpl implements HttpOutputMessage{
private final OutputStream body;
private final HttpHeaders headers;
public HttpOutputMessageImpl(OutputStream body, HttpHeaders headers) {
this.body = body;
this.headers = headers;
}
@Override
public OutputStream getBody() throws IOException {
return body;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}
static boolean isFormRequest(Type type){
return MAP_STRING_WILDCARD.equals(type);
}
static class MultipartFileResource extends InputStreamResource {
private final String filename;
private final long size;
public MultipartFileResource(InputStream inputStream, String filename, long size) {
super(inputStream);
this.filename = filename;
this.size = size;
}
@Override
public String getFilename() {
return this.filename;
}
@Override
public InputStream getInputStream() throws IOException, IllegalStateException {
return super.getInputStream();
}
@Override
public long contentLength(){
return size;
}
}
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
if (isFormRequest(bodyType)){
final HttpHeaders multipartHeaders = new HttpHeaders();
multipartHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
encodeMultipartFormRequest((Map
具体实现代码段
private HttpEntity> serializeObject(Object obj){
HttpHeaders jsonPartHeaders = new HttpHeaders();
Object content;
if (obj instanceof JdkSerializable){
//需要jdk则使用jdk编码
jsonPartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
content = SerializationUtils.serialize(obj);
}else {
//否则默认使用json
jsonPartHeaders.setContentType(MediaType.APPLICATION_JSON);
content = JSON.toJSonString(obj);
}
return new HttpEntity<>(content, jsonPartHeaders);
}
package cn.ssq.http.model;
import java.io.Serializable;
public interface JdkSerializable extends Serializable {
}
我们需要重新封装QueryWrapper的DTO对象并实现自定义的JdkSerializable接口
package cn.ssq.dto; import cn.ssq.http.model.JdkSerializable; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; public class QueryWrapperDTO服务端代码(接受方)extends QueryWrapper implements JdkSerializable { }
springmvc默认没有jdk反序列化对象转化实现,所以我们需要自己去实现jdk的反序列化
package cn.ssq.http.converter; import cn.ssq.http.model.JdkSerializable; import org.springframework.amqp.utils.SerializationUtils; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.AbstractHttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.remoting.rmi.CodebaseAwareObjectInputStream; import org.springframework.util.ClassUtils; import org.springframework.util.StreamUtils; import java.io.*; import java.util.linkedHashSet; import java.util.Properties; import java.util.Set; public class JdkSerializableHttpMessageConverter extends AbstractHttpMessageConverter{ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); private final Set whiteListPatterns = new linkedHashSet(); public JdkSerializableHttpMessageConverter() { super(new MediaType[]{new MediaType("application", "octet-stream"), MediaType.ALL}); } public boolean supports(Class> clazz) { Class>[] interfaces = clazz.getInterfaces(); for (Class> anInterface : interfaces) { if (anInterface.equals(JdkSerializable.class)){ return true; } } return false; } @Override public Object readInternal(Class> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { long contentLength = inputMessage.getHeaders().getContentLength(); ByteArrayOutputStream bos = new ByteArrayOutputStream(contentLength >= 0L ? (int)contentLength : 4096); StreamUtils.copy(inputMessage.getBody(), bos); //使用jdk序列化,需要使用jdk反序列化,原理:读取class池中class对象的并构造实例对象 Object deserialize = SerializationUtils.deserialize(this.createObjectInputStream(new ByteArrayInputStream(bos.toByteArray()), null)); return deserialize; } @Override protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException { OutputStream os = outputMessage.getBody(); Properties properties = new Properties(); properties.store(os, String.valueOf(SerializationUtils.serialize(obj))); } protected ObjectInputStream createObjectInputStream(InputStream is, String codebaseUrl) throws IOException { return new CodebaseAwareObjectInputStream(is, this.beanClassLoader, codebaseUrl) { //根据路径解析获取class对象 protected Class> resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException { Class> clazz = super.resolveClass(classDesc); return clazz; } }; } }
package cn.ssq.http.config;
import cn.ssq.http.converter.JdkSerializableHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List> converters) {
converters.add(new JdkSerializableHttpMessageConverter());
}
}



