Principios de serialización y deserialización de Java y explicación detallada de las tecnologías de serialización comunes

En el curso normal del trabajo, a menudo mencionamos y usamos serialización y deserialización. El uso de RPC es el mejor ejemplo. Entonces, ¿qué es serialización y deserialización? Hay serialización y deserialización. ¿Cuál es el uso y cómo implementa Java la serialización y deserialización?

La serialización es el proceso de convertir la información de estado de un objeto en una forma que pueda ser almacenada o transmitida, simplemente es el proceso de convertir un objeto en una secuencia de bytes. La deserialización es el proceso inverso de la serialización, el proceso de deserialización de una matriz de bytes en un objeto y restaurarla en un objeto. Entonces, ¿qué problemas resuelven la serialización y la deserialización?

El objeto se serializa en una matriz de bytes, el objeto se puede utilizar para la transmisión entre procesos a través de la red y el objeto se puede almacenar permanentemente en un dispositivo de almacenamiento. Este es el principal problema que resuelve la serialización: problemas de almacenamiento y de transmisión.

A continuación, usamos el método de serialización nativo de Java para escribir un ejemplo simple de serialización y deserialización, la serialización de Java se logra principalmente a través de java.io.ObjectOutputStream y java.io.ObjectInputStream, entre los cuales el objeto serializado necesita implementar java .io.Serializable interfaz, de la siguiente manera, definimos un objeto e implementamos la interfaz serializable:

public class User implements Serializable {
    private static final long serialVersionUID = 2925461581469771001L;
    private int id;
    private String name;
    ......//setter和getter方法

}

A continuación escribimos el código para la serialización y deserialización de objetos. Aquí usaremos las clases ObjectOutputStream y ObjectInputStream escritas anteriormente. El código de muestra es el siguiente:

public class JdkSerializerImpl<T> implements ISerializer<T> {
    //序列化
    @Override
    public <T> byte[] serizlize(T obj) throws IOException {
	ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
	ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
	objectOutputStream.writeObject(obj);
	return byteArrayOutputStream.toByteArray();
}
	//反序列化
    @Override
    public <T> T unserizlize(byte[] data) throws ClassNotFoundException, IOException {
	ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
	ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
	return (T) objectInputStream.readObject();
    }

}

Arriba usamos el método de serialización nativo de Java para escribir un ejemplo de serialización de Java. A continuación usamos Socket como un ejemplo para introducir objetos que usan Socket para transmitir a través de la red. Transmitimos objetos de Usuario en el cliente y recibimos los serializados en el servidor. Después del objeto Usuario, deserialice e imprima la información del usuario. El siguiente es el código del cliente:

//连接socket
Socket socket = new Socket("127.0.0.1", 9090);
//创建User对象
User user = new User();
user.setId(1);
user.setName("pharos");
//通过Socket传输对象
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(user);
socket.shutdownOutput();
//接收服务端传输的对象
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
user = (User) objectInputStream.readObject();
//打印传出的传输的对象User [id=1, name=receive]
System.out.println(user.toString());
socket.close();

El código anterior es un cliente de Socket, que envía el objeto Usuario al servidor a través del Socket y recibe el objeto Usuario modificado del servidor. Continuemos viendo el código del servidor:

//创建服务端
ServerSocket serverSocket = new ServerSocket(9090);
while(true) {
    //接收Socket
    Socket socket = serverSocket.accept();
    //获取Socket传输过来的User对象
    InputStream in = socket.getInputStream();
    ObjectInputStream objectInputStream = new ObjectInputStream(in);
    //通过反序列化获取User对象
    User user = (User) objectInputStream.readObject();
    //打印接收到的User对象
    System.out.println(user.toString());
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
    //修改User名称
    user.setName("receive");
    //将User对象传输到客户端
    objectOutputStream.writeObject(user);
}

El código anterior es la llamada RPC más simple, y también es el prototipo de muchos protocolos RPC, pero el procesamiento del marco RPC es más complicado y su núcleo básico no es más que la transmisión y deserialización de objetos serializados. Echemos un vistazo a los principios de serialización y deserialización, es decir, el método readObject () y el método writeObject (obj) utilizados anteriormente.

 public final Object readObject()throws IOException, ClassNotFoundException{
    .....
    try {
        //反序列化主要逻辑在readObject0()方法中
        Object obj = readObject0(false);
        ......//其他逻辑
    }
    ......//其他逻辑
}

A partir del código anterior, podemos ver que la lógica principal de deserialización está en el método readObject0 (). Continuemos mirando el método readObject0 (). En este método, diferentes tipos de objetos llaman a diferentes métodos para la deserialización. El código central es el siguiente:

switch (tc) {
    //如果为NULL类型
    case TC_NULL:
        return readNull();
    ......//其他类型
	//枚举类型
    case TC_ENUM:
        return checkResolve(readEnum(unshared));
	//Object类型
    case TC_OBJECT:
        return checkResolve(readOrdinaryObject(unshared));
	//其他类型
}

El código anterior llama al método readOrdinaryObject (), y el método final llamado es

void invokeReadObjectNoData(Object obj)throws IOException, UnsupportedOperationException{
    requireInitialized();
    if (readObjectNoDataMethod != null) {
        try {
            readObjectNoDataMethod.invoke(obj, (Object[]) null);
        } catch (InvocationTargetException ex) {
            Throwable th = ex.getTargetException();
            if (th instanceof ObjectStreamException) {
                throw (ObjectStreamException) th;
            } else {
                throwMiscException(th);
            }
        } catch (IllegalAccessException ex) {
            // should not occur, as access checks have been suppressed
            throw new InternalError(ex);
        }
    } else {
        throw new UnsupportedOperationException();
    }
}

A través del código fuente, podemos ver que readObject finalmente se realiza a través de la reflexión. De hecho, podemos ver el uso de readObject y writeObject en muchos lugares, como HashMap. De manera similar, writeObject finalmente llama a invokeWriteObject, que también se realiza mediante la reflexión.

void invokeWriteObject(Object obj, ObjectOutputStream out)throws IOException, UnsupportedOperationException{
    requireInitialized();
    if (writeObjectMethod != null) {
        try {
            writeObjectMethod.invoke(obj, new Object[]{ out });
        } catch (InvocationTargetException ex) {
            Throwable th = ex.getTargetException();
            if (th instanceof IOException) {
                throw (IOException) th;
            } else {
                throwMiscException(th);
            }
        } catch (IllegalAccessException ex) {
            // should not occur, as access checks have been suppressed
            throw new InternalError(ex);
        }
    } else {
        throw new UnsupportedOperationException();
    }
}

Ya hemos aprendido sobre la serialización de Java. Además del esquema de serialización proporcionado por Java mismo, actualmente existen muchos otros marcos de serialización de código abierto, como el marco de serialización XML, el marco de serialización JSON, el marco de serialización Hessian, Avro Serialización, marco de serialización kyro, marco de serialización Protobuf, etc.

La ventaja de la serialización XML es que es legible y XML es independiente del idioma, por lo que también se puede utilizar para el intercambio de datos y acuerdos entre sistemas heterogéneos. Por ejemplo, el
conocido Webservice utiliza formato XML para serializar datos. Hay muchas formas de implementar la serialización / deserialización XML. Los métodos más conocidos incluyen XStream y la serialización y deserialización XML propia de Java. No presentaremos los métodos de uso específicos aquí.

Hay muchas herramientas de código abierto que se utilizan comúnmente para la serialización JSON: Jackson (https://github.com/FasterXML/jackson), Ali de código abierto FastJson, GSON de Google (https://github.com/google/gson) estas secuencias json Entre las herramientas químicas, Jackson y fastjson tienen un mejor rendimiento que GSON, pero Jackson y GSON tienen mejor estabilidad que Fastjson. La ventaja de fastjson es que la API proporcionada es muy fácil de usar. Para su uso específico, consulte el resumen del marco JSON .

Ya no se introducen otros marcos. Aquí presentamos el uso simple de Hessian. De hecho, Dubbo usa la serialización Hessian para lograrlo, pero Dubbo refactoriza Hessian con un rendimiento más alto. Hessian es una secuencia binaria que se transmite a través de idiomas En comparación con el mecanismo de serialización predeterminado de Java, Hessian tiene un buen rendimiento y facilidad de uso y es compatible con una variedad de lenguajes diferentes. Entre ellos, AbstractSerializerFactory, AbstractHessianOutput, AbstractSerializer, AbstractHessianInput, AbstractDeserializer son sus clases principales. Usemos un ejemplo a continuación. Introduzca el uso de arpillera:

public <T> byte[] serizlize(T obj) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    HessianOutput hessianOutput = new HessianOutput(outputStream);
    hessianOutput.writeObject(obj);
    return outputStream.toByteArray();
}
public <T> T unserizlize(byte[] data) throws ClassNotFoundException, IOException {
    ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
    HessianInput input = new HessianInput(inputStream);
    return (T) input.readObject();
}

 

 

Supongo que te gusta

Origin blog.csdn.net/wk19920726/article/details/108748147
Recomendado
Clasificación