栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用jdk序列化的方式实现微服务之间抽象类的数据传输

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用jdk序列化的方式实现微服务之间抽象类的数据传输

根据日常业务需求微服务之间数据传输对象可能是比较复杂的对象,就以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) object, multipartHeaders, template);
        } else {
            final HttpHeaders jsonHeaders = new HttpHeaders();
            jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
            encodeRequest(object, jsonHeaders, template);
        }
    }

    
    private void encodeMultipartFormRequest(Map formMap, HttpHeaders multipartHeaders, RequestTemplate template){
        if (formMap == null){
            throw new EncodeException("无法对格式为null的请求进行编码。");
        }

        linkedMultiValueMap map = new linkedMultiValueMap<>();
        //对每个参数进行检查校验
        for (Entry entry : formMap.entrySet()){
            Object value = entry.getValue();
            //不同的数据类型进行不同的编码逻辑处理
            if (isMultipartFile(value)){
                //单个文件
                map.add(entry.getKey(), encodeMultipartFile((MultipartFile)value));

            } else if (isMultipartFileArray(value)){
                //多个文件
                encodeMultipartFiles(map, (String) entry.getKey(), Arrays.asList((MultipartFile[]) value));

            } else {
                //普通请求数据
                map.add(entry.getKey(), serializeObject(value));
            }
        }

        encodeRequest(map, multipartHeaders, template);
    }

    
    private void encodeRequest(Object value, HttpHeaders requestHeaders, RequestTemplate template){
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        HttpOutputMessage dummyRequest = new HttpOutputMessageImpl(outputStream, requestHeaders);

        try {
            Class requestType = value.getClass();
            MediaType requestContentType = requestHeaders.getContentType();
            for (HttpMessageConverter messageConverter : converters){
                if (messageConverter.canWrite(requestType, requestContentType)){
                    ((HttpMessageConverter) messageConverter).write(value, requestContentType, dummyRequest);
                    break;
                }
            }
        } catch (IOException e) {
            throw new EncodeException("无法对请求进行编码:", e);
        }

        HttpHeaders headers = dummyRequest.getHeaders();
        if (headers != null){
            for (Entry> entry : headers.entrySet()){
                template.header(entry.getKey(), entry.getValue());
            }
        }

        
        template.body(outputStream.toByteArray(), UTF_8);
    }

    
    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);
    }

    
    private void encodeMultipartFiles(linkedMultiValueMap map, String name, List fileList){
        HttpHeaders filePartHeaders = new HttpHeaders();
        //设置 Content-type
        filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            for (MultipartFile file : fileList){
                Resource multipartFileResource = new MultipartFileResource(file.getInputStream(), file.getOriginalFilename(), file.getSize());
                map.add(name, new HttpEntity<>(multipartFileResource, filePartHeaders));
            }
        } catch (IOException e) {
            throw new EncodeException("无法对请求进行编码:", e);
        }
    }

    
    private HttpEntity encodeMultipartFile(MultipartFile file){
        HttpHeaders filePartHeaders = new HttpHeaders();
        //设置 Content-type
        filePartHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        try {
            Resource multipartFileResource = new MultipartFileResource(file.getInputStream(), file.getOriginalFilename(), file.getSize());
            return new HttpEntity<>(multipartFileResource, filePartHeaders);
        } catch (IOException e) {
            throw new EncodeException("无法对请求进行编码:", e);
        }
    }

    
    private boolean isMultipartFileArray(Object object){
        return object != null && object.getClass().isArray() && MultipartFile.class.isAssignableFrom(object.getClass().getComponentType());
    }

    
    private boolean isMultipartFile(Object object){
        return object instanceof MultipartFile;
    }


} 

具体实现代码段

 
    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());
    }
}

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号