前言

序列化:将java对象转化为可传输的字节数组

反序列化:将字节数组还原为java对象

为啥子要序列化?

序列化最终的目标是为了对象能够跨平台存储,和进行网络传输。而咱们进行跨平台存储和网络传输的形式就是IO,而咱们的IO反对的数据格式就是字节数组

什么状况下须要序列化?

但凡须要进行跨平台存储和网络传输的数据,都须要进行序列化

实质上存储和网络传输 都须要通过 把一个对象状态保留成一种跨平台辨认的字节格局,而后其余的平台才能够通过字节信息解析还原对象信息

序列化的形式

序列化只是一种拆装组装对象的规定,这种规定多种多样,常见的序列化形式有:

JDK(不反对跨语言)、JSON、XML、Hessian、Kryo(不反对跨语言)、Thrift、Protostuff、FST(不反对跨语言)

举个栗子

自定义协定中,须要序列化和反序列化,案例中枚举类Algorithm的外部类重写了自定义接口Serializer中的序列化和反序列化办法,本案例中枚举类Algorithm采纳了jdk和json两种序列化形式,通过配置类Config类,能够灵便在application.properties中抉择序列化的形式

导入依赖

<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --><dependency>    <groupId>com.google.code.gson</groupId>    <artifactId>gson</artifactId>    <version>2.8.5</version></dependency>

自定义Message类

package com.lian.chatroom.message;import lombok.Data;import java.io.Serializable;import java.util.HashMap;import java.util.Map;@Datapublic abstract class Message implements Serializable {    private int sequenceId;    private int messageType;    /**     * 依据音讯类型 的 数字编号,取得对应的音讯 class     * @param messageType 音讯类型字节     * @return 音讯 class     */    public static Class<? extends Message> getMessageClass(int messageType) {        return messageClasses.get(messageType);    }    //定义形象办法,获取返回音讯类型    public abstract int getMessageType();    //自定义动态常量,每种数据类型以数字代表    public static final int LoginRequestMessage = 0;    public static final int LoginResponseMessage = 1;    public static final int ChatRequestMessage = 2;    public static final int ChatResponseMessage = 3;    public static final int GroupCreateRequestMessage = 4;    public static final int GroupCreateResponseMessage = 5;    public static final int GroupJoinRequestMessage = 6;    public static final int GroupJoinResponseMessage = 7;    public static final int GroupQuitRequestMessage = 8;    public static final int GroupQuitResponseMessage = 9;    public static final int GroupChatRequestMessage = 10;    public static final int GroupChatResponseMessage = 11;    public static final int GroupMembersRequestMessage = 12;    public static final int GroupMembersResponseMessage = 13;    public static final int PingMessage = 14;    public static final int PongMessage = 15;    /**     * 申请类型 byte 值     */    public static final int RPC_MESSAGE_TYPE_REQUEST = 101;    /**     * 响应类型 byte 值     */    public static final int  RPC_MESSAGE_TYPE_RESPONSE = 102;    //map存储(音讯类型数字编号,音讯类型)    private static final Map<Integer, Class<? extends Message>> messageClasses = new HashMap<>();    //static代码块随着类的加载而执行,而且只执行一次    static {        messageClasses.put(LoginRequestMessage, LoginRequestMessage.class);        messageClasses.put(LoginResponseMessage, LoginResponseMessage.class);        messageClasses.put(ChatRequestMessage, ChatRequestMessage.class);        messageClasses.put(ChatResponseMessage, ChatResponseMessage.class);        messageClasses.put(GroupCreateRequestMessage, GroupCreateRequestMessage.class);        messageClasses.put(GroupCreateResponseMessage, GroupCreateResponseMessage.class);        messageClasses.put(GroupJoinRequestMessage, GroupJoinRequestMessage.class);        messageClasses.put(GroupJoinResponseMessage, GroupJoinResponseMessage.class);        messageClasses.put(GroupQuitRequestMessage, GroupQuitRequestMessage.class);        messageClasses.put(GroupQuitResponseMessage, GroupQuitResponseMessage.class);        messageClasses.put(GroupChatRequestMessage, GroupChatRequestMessage.class);        messageClasses.put(GroupChatResponseMessage, GroupChatResponseMessage.class);        messageClasses.put(GroupMembersRequestMessage, GroupMembersRequestMessage.class);        messageClasses.put(GroupMembersResponseMessage, GroupMembersResponseMessage.class);        messageClasses.put(RPC_MESSAGE_TYPE_REQUEST, RpcRequestMessage.class);        messageClasses.put(RPC_MESSAGE_TYPE_RESPONSE, RpcResponseMessage.class);    }}

自定义序列化接口

自定义枚举类Algorithm,而枚举类Algorithm也有两个外部类对象 java和json,别离重写了接口的序列化和反序列化办法

package com.lian.chatroom.protocol;import com.google.gson.Gson;import java.io.*;import java.nio.charset.StandardCharsets;/** * 为了反对更多的序列化办法 */public interface Serializer {    /**     * 反序列化     * 将byte[]或json 转换为 java对象     * @param bytes 字节数组     * @param clazz 要转换成的java对象类型     * @param <T> 泛型     * @return     */    <T> T deSerializer(byte[] bytes, Class<T> clazz);    /**     * 序列化     * 将java对象 转换为 byte[]或json类型     */    <T> byte[] serializer(T object);    /**     * 创立外部枚举类 Algorithm,实现序列化     */    enum Algorithm implements Serializer{        //java代表是自带jdk的序列化与反序列化        java{            @Override            public <T> T deSerializer(byte[] bytes, Class<T> clazz) {                try {                    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));                    //对象输入流读取java对象                    return (T) ois.readObject();                } catch (IOException | ClassNotFoundException e) {                    e.printStackTrace();                    throw new RuntimeException("反序列化失败", e);                }            }            @Override            public <T> byte[] serializer(T object) {                try {                    ByteArrayOutputStream bos = new ByteArrayOutputStream();                    ObjectOutputStream oos = new ObjectOutputStream(bos);                    //将java对象写入到对象输入流中                    oos.writeObject(object);                    byte[] bytes = bos.toByteArray(); //返回字节数组                    return bytes;                } catch (IOException e) {                    throw new RuntimeException("序列化失败", e);                }            }        },        json{            @Override            public <T> T deSerializer(byte[] bytes, Class<T> clazz) {                //将字节数组转换为字符串                String json = new String(bytes, StandardCharsets.UTF_8);                return new Gson().fromJson(json,clazz);            }            @Override            public <T> byte[] serializer(T object) {                Gson gson = new Gson();                //将java对象转化为json字符串                String json = gson.toJson(object);                //将json字符串转换为字节数组                return json.getBytes(StandardCharsets.UTF_8);            }        }    }}

自定义协定类

自定义的协定里须要编解码,序列化的形式,此处抉择了jdk和json

package com.lian.chatroom.protocol;import com.lian.chatroom.config.Config;import com.lian.chatroom.message.Message;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandler;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToMessageCodec;import lombok.extern.slf4j.Slf4j;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.util.List;/** * 必须和 LengthFieldBasedFrameDecoder 一起应用,确保接到的 ByteBuf 音讯是残缺的 * 音讯编解码 * 出栈:ByteBuf格局数据 转换为 字符串等其余格局 解码 * 入栈:字符串等其余格局 转换为  ByteBuf格局数据 编码 */@Slf4j@ChannelHandler.Sharablepublic class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {    @Override    protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {        //用通道调配一个缓存区        ByteBuf out = ctx.alloc().buffer();        //1. 4 字节的魔数,就是服务端和客户端约定好的暗号,例如:天王盖地虎 宝塔镇魔妖        out.writeBytes(new byte[]{1, 2, 3, 4});        // 2. 1 字节的版本,        out.writeByte(1);        // 3. 1 字节的序列化形式 jdk 0 , json 1        //out.writeByte(0); //写死的形式        //3.1 采纳配置类灵便抉择序列化形式,返回此枚举常量的序号,如果序列化形式是jdk就会填写0,如果是json就会填写1        out.writeByte(Config.getSerializerAlgorithm().ordinal());        // 4. 1 字节的指令类型        out.writeByte(msg.getMessageType());        // 5. 4 个字节        out.writeInt(msg.getSequenceId());        // 无意义,对齐填充        out.writeByte(0xff);        // 6. 获取内容的字节数组//        ByteArrayOutputStream bos = new ByteArrayOutputStream();//        ObjectOutputStream oos = new ObjectOutputStream(bos);//        oos.writeObject(msg);//        byte[] bytes = bos.toByteArray();        //6.1、采纳jdk形式序列化,将java对象转为字节数组        //byte[] bytes = Serializer.Algorithm.java.serializer(msg);        //6.2、采纳json形式序列化        //byte[] bytes = Serializer.Algorithm.json.serializer(msg);        //6.3、采纳配置类模式,来灵便抉择应用哪种 序列化形式        byte[] bytes = Config.getSerializerAlgorithm().serializer(msg);        // 7. 长度        out.writeInt(bytes.length);        // 8. 将字节数组写入到缓存区        out.writeBytes(bytes);        outList.add(out);    }    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {        int magicNum = in.readInt();        byte version = in.readByte();        //从缓存区中读取到编码时用的哪种序列化算法类型,是jdk or json        //返回 0 or 1, 0代表jdk序列化形式,1代表json序列化形式        byte serializerAlgorithm = in.readByte();        //音讯类型,0,1,2,。。。        byte messageType = in.readByte();        int sequenceId = in.readInt();        //从缓存区读取字节数组数据        in.readByte();        //获取缓存区内字节数组的大小        int length = in.readInt();        //生成和缓冲区数据大小雷同的byte数组,将缓存区内数据 封装到 byte数组        byte[] bytes = new byte[length];        in.readBytes(bytes, 0, length);//        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));//        Message message = (Message) ois.readObject();        //采纳jdk形式反序列化,将byte数组转为Message对象        //Message message = Serializer.Algorithm.java.deSerializer(bytes, Message.class);        //采纳json形式反序列化        //Message message = Serializer.Algorithm.json.deSerializer(bytes, Message.class);        //采纳配置类灵便抉择应用哪种序列化形式进行解码        //values返回全副序列化形式,下标为0就是jdk形式,下标为1就是json形式,必须和序列化的编解码形式雷同        //Serializer.Algorithm.values()[serializerAlgorithm] 找到反序列化形式算法,是jdk还是json        //Message.getMessageClass(messageType) 确定具体音讯类型        Message message = Serializer.Algorithm.values()[serializerAlgorithm].deSerializer(bytes, Message.getMessageClass(messageType));        log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerAlgorithm, messageType, sequenceId, length);        log.debug("{}", message);        out.add(message);    }}

配置类Config

依据搭配application.properties,可灵便抉择序列化的形式

package com.lian.chatroom.config;import com.lian.chatroom.protocol.Serializer;import java.io.IOException;import java.io.InputStream;import java.util.Properties;/** * 此类作用 * 序列化形式有很多种,配置类能够灵便设置 选用哪种序列化形式,代替间接在 MessageCodecSharable协定类里批改 */public abstract class Config {    static Properties properties;    static {        try {            //加载本类下的资源文件            InputStream inputStream = Config.class.getResourceAsStream("/application.properties");            properties = new Properties();            properties.load(inputStream);        } catch (IOException e) {            throw new RuntimeException(e);        }    }    public static int getSetverPort(){        String value = properties.getProperty("server.port");        if (value == null){            return 8080;        }else {            return Integer.parseInt(value);//            return Integer.valueOf(value);        }    }    public static Serializer.Algorithm getSerializerAlgorithm(){        String value = properties.getProperty("serializer.algorithm");        if (value == null){            return Serializer.Algorithm.java;        }else {            return Serializer.Algorithm.valueOf(value);        }    }}

application.properties

#如果为null,默认是8080server.port=8080#如果为空,默认是 jdk的序列化形式serializer.algorithm=json

测试

package com.lian.chatroom;import com.lian.chatroom.message.LoginRequestMessage;import com.lian.chatroom.protocol.MessageCodecSharable;import io.netty.channel.embedded.EmbeddedChannel;import io.netty.handler.logging.LoggingHandler;import org.junit.jupiter.api.Test;public class TestSerializer {    @Test    public void encode() {        MessageCodecSharable Codec = new MessageCodecSharable();        LoggingHandler LOGGING = new LoggingHandler();        //EmbeddedChannel是netty专门改良针对ChannelHandler的单元测试而提供的        EmbeddedChannel channel = new EmbeddedChannel(LOGGING, Codec, LOGGING);        LoginRequestMessage message = new LoginRequestMessage("zhangsan", "123");        channel.writeOutbound(message);    }}

实体类

package com.lian.chatroom.message;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;/** * 登录申请音讯,须要用户名和明码 * * 客户端和服务端建立联系后,客户端向服务端发送一个登录申请的音讯 * 用户名和明码正确,登录胜利,持续进行下一步聊天业务 * 登录失败,就退出提醒从新登录 */@Data@AllArgsConstructor@NoArgsConstructor@ToString(callSuper = true)public class LoginRequestMessage extends Message{    private String username;    private String password;    //获取音讯类型    @Override    public int getMessageType() {        return LoginRequestMessage;    }}

最初

在文章的最初作者为大家整顿了很多材料!包含java外围知识点+全套架构师学习材料和视频+一线大厂面试宝典+面试简历模板+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书等等!

材料都会相对收费分享给大家的,只心愿你给作者点个三连!

欢送关注公众号:前程有光,支付!