preface
Serialization: Converts Java objects into transportable byte arrays
Deserialization: Restores byte arrays to Java objects
Why serialize?
The ultimate purpose of serialization is for objects to be stored across platforms and transported over networks. IO is our way of cross-platform storage and network transmission, and the data format supported by IO is byte array
When is serialization needed?
Any data that needs to be stored cross-platform or transferred over the network needs to be serialized
In essence, storage and network transport require an object state to be saved into a cross-platform byte format, and then other platforms can parse the object through byte information
Serialization
Serialization is just a rule for disassembling and assembling objects. There are many kinds of rules. The common serialization methods are:
JDK (not supported across languages), JSON, XML, Hessian, Kryo (not supported across languages), Thrift, Protostuff, FST (not supported across languages)
Take a chestnut
In this case, the internal class of enumeration Algorithm overrides the serialization and deserialization methods in Serializer. In this case, the enumeration Algorithm uses JDK and JSON serialization methods. You have the flexibility to choose how to serialize in application.properties
Import dependence
<! -- 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 >Copy the code
Customize the Message class
package com.lian.chatroom.message; import lombok.Data; import java.io.Serializable; import java.util.HashMap; import java.util.Map; @Data public abstract class Message implements Serializable { private int sequenceId; private int messageType; Class * @param messageType messageType byte * @return message 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 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; Public static final int RPC_MESSAGE_TYPE_REQUEST = 101; public static final int RPC_MESSAGE_TYPE_REQUEST = 101; Public static final int RPC_MESSAGE_TYPE_RESPONSE = 102; public static final int RPC_MESSAGE_TYPE_RESPONSE = 102; Private static final map <Integer, Class<? extends Message>> messageClasses = new HashMap<>(); / / static code block as the class loading and execution, and only perform a 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); }}Copy the code
Custom serialization interface
The enumeration Algorithm has two inner class objects, Java and JSON, which overwrite the serialization and deserialization methods of the interface, respectively
package com.lian.chatroom.protocol; import com.google.gson.Gson; import java.io.*; import java.nio.charset.StandardCharsets; /** * To support more serialization methods */ public Interface Serializer {/** * deserialize * Convert Byte [] or JSON to A Java object * @param Bytes array * @param Clazz Specifies the Java object type to be converted to * @param <T> generic * @return */ <T> T deSerializer(byte[] bytes, Class<T> clazz); /** * serialize * Convert Java objects to byte[] or JSON */ <T> byte[] serializer(T object); /** * Create an internal enumeration Algorithm, */ enum Algorithm implements Serializer{@override public <T> T deSerializer(byte[] bytes, Class<T> clazz) { try { ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); Return (T) ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); Throw new RuntimeException(" deserialization failed ", e); } } @Override public <T> byte[] serializer(T object) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); // Write a Java object to the object output stream oos.writeObject(object); byte[] bytes = bos.toByteArray(); // Return bytes; } catch (IOException e) {throw new RuntimeException(" serialization failed ", e); } } }, json{ @Override public <T> T deSerializer(byte[] bytes, Class<T> clazz) {// Convert byte array to 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(); // convert Java objects toJson strings String json = gson.tojson (object); Return json.getBytes(standardCharsets.utf_8); }}}}Copy the code
Custom protocol classes
Custom protocol needs encoding and decoding, serialization, JDK and JSON are selected here
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; / * * * must be used together with LengthFieldBasedFrameDecoder, ensure receiving ByteBuf news is complete codec * * news out of the stack: ByteBuf format data is converted to a string and other format decoding * into the stack: */ @[email protected] Public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> { @Override protected void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {// Allocate a buffer with a channel ByteBuf out = ctx.alloc().buffer(); WriteBytes (new byte[]{1, 2, 3, 4}); writeBytes(new byte[]{1, 2, 3, 4}); // The 2.1-byte version, out.writebyte (1); JDK 0, json 1 // out.writebyte (0); The serial number of this enumeration constant is returned. If the serial number of this enumeration constant is JDK, fill in 0. If it is a json will fill in 1 out. WriteByte (Config. GetSerializerAlgorithm (). The ordinal ()); Out.writebyte (msg.getMessageType()); WriteInt (msg.getSequenceId()); // meaningless, align padding out.writebyte (0xff); Bos = new ByteArrayOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(bos); // oos.writeObject(msg); // byte[] bytes = bos.toByteArray(); / / 6.1, using the JDK serialization, the Java object into a byte array / / byte [] bytes = Serializer. Algorithm. Java. The Serializer (MSG); / / 6.2, using json serialization / / byte [] bytes = Serializer. Algorithm. Json. The Serializer (MSG); / / 6.3, USES the configuration class form, the flexibility to choose to use which kinds of way of serialized byte [] bytes = Config. GetSerializerAlgorithm (). The serializer (MSG); // 7. Length out.writeint (bytes.length); // 8. Write the byte array to the cache 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(); Bytes serializerAlgorithm = in.readbyte (); bytes serializerAlgorithm = in.readbyte (); // message type, 0,1,2... byte messageType = in.readByte(); int sequenceId = in.readInt(); In.readbyte (); Int length = in.readint (); Byte [] bytes = new byte[length]; // Generate a byte array with the same size as the buffer data and encapsulate the cache data in the byte array. in.readBytes(bytes, 0, length); // ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes)); // Message message = (Message) ois.readObject(); / / using the JDK deserialization, a byte array to the Message object / / Message Message = Serializer. Algorithm. Java. The deSerializer (bytes, Message. Class); / / using json deserialize / / Message Message = Serializer. Algorithm. Json. The deSerializer (bytes, Message. Class); //values returns all serialization methods, subscript 0 is JDK method, subscript 1 is JSON method, Must be the same, and serialization of decoding way. / / Serializer Algorithm. The values () [serializerAlgorithm] find ways to deserialize Algorithm, Is the JDK or json / / Message. GetMessageClass (messageType) identify a specific Message type 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); }}Copy the code
The configuration class Config
With application.properties, you can choose the serialization method flexibly
package com.lian.chatroom.config; import com.lian.chatroom.protocol.Serializer; import java.io.IOException; import java.io.InputStream; import java.util.Properties; There are many different serialization methods, and the configuration class has the flexibility to choose which one to serialize. Public abstract class Config {static Properties Properties; Static {try {/ / load under this kind of resource file InputStream InputStream = Config. The class. The 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); }}}Copy the code
application.properties
Port =8080 server. Port =8080 server. Port =8080 ServerCopy the code
test
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 = new EmbeddedChannel(LOGGING, Codec, LOGGING); LoginRequestMessage message = new LoginRequestMessage("zhangsan", "123"); channel.writeOutbound(message); }}Copy the code
Entity class
package com.lian.chatroom.message; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * Login request message, which requires user name and password ** After the client establishes contact with the server, the client sends a login request message to the server. * The user name and password are correct, login succeeds, and continue to chat. * Login fails. Constructor @noargsconstructor @toString (callSuper = true) public class LoginRequestMessage extends Message{ private String username; private String password; @override public int getMessageType() {return LoginRequestMessage; }}Copy the code
The last
At the end of the article the author sorted out a lot of information for you! Including Java core knowledge + a full set of architect learning materials and video + first-line factory interview treasure dictionary + resume template interview ali Meituannetease Tencent Xiaomi IQiyi Quick hand bilibili bilibili interview questions +Spring source code collection +Java architecture actual combat e-books and so on!
Information will be absolutely free to share to everyone, only hope you give the author a point three even!
Welcome to pay attention to the public number: the future has light, receive!