Nueva serializaci贸n puede aparecer en Java

En el sitio web de OpenJDK apareci贸 un nuevo documento de investigaci贸n que describe la idea de introducir una nueva serializaci贸n mejorada en el lenguaje para reemplazar el antiguo.

La serializaci贸n en Java existe desde la versi贸n 1.1, es decir, casi desde el momento en que naci贸. Por un lado, la serializaci贸n es un mecanismo muy conveniente que le permite hacer r谩pida y f谩cilmente cualquier clase serializable heredando esta clase de la interfaz java.io.Serializable. Quiz谩s incluso esta simplicidad se haya convertido en una de las razones clave por las que Java ha ganado tanta popularidad en el mundo, porque le permite escribir aplicaciones de red de manera r谩pida y eficiente.

Por otro lado, la forma en que se implementa la serializaci贸n en Java implica una gran cantidad de problemas que aumentan el costo de soporte de aplicaciones, reducen su seguridad y ralentizan la evoluci贸n de la plataforma.

驴Qu茅 hay de malo con la serializaci贸n en Java? Enumeramos los problemas m谩s serios:

  • La serializaci贸n (y la deserializaci贸n) evita los mecanismos del lenguaje. Ignora los modificadores de acceso de campo (privados, protegidos) y crea objetos sin usar constructores, lo que significa que ignora los invariantes que pueden estar presentes en estos constructores. Un atacante podr铆a explotar tal vulnerabilidad al sustituir los datos con datos no v谩lidos, y se los tragar铆a con 茅xito durante la deserializaci贸n.
  • Al escribir clases serializables, el compilador no ayuda de ninguna manera y no detecta errores. Por ejemplo, no puede garantizar est谩ticamente que todos los campos de una clase serializable sean ellos mismos serializables. O puede hacer un error tipogr谩fico en los nombres de los m茅todos readObject, writeObject, readResolve, etc., y estos m茅todos simplemente no se utilizar谩n durante la serializaci贸n.
  • La serializaci贸n no admite el mecanismo normal de versiones, por lo que es muy dif铆cil modificar las clases serializables para que sigan siendo compatibles con sus versiones anteriores.
  • La serializaci贸n est谩 fuertemente ligada a la codificaci贸n / decodificaci贸n de transmisi贸n, lo que significa que cambiar el formato de codificaci贸n a uno diferente del est谩ndar es muy dif铆cil. Adem谩s, el formato est谩ndar no es compacto, ni eficiente, ni legible para humanos.

El error fundamental de la serializaci贸n existente en Java es que intenta ser demasiado "invisible" para el programador. Simplemente hereda de java.io.Serializable y recibe alg煤n tipo de magia impl铆cita que es ejecutada por la m谩quina virtual.
Por el contrario, el programador debe escribir expl铆citamente construcciones responsables de construir y deconstruir objetos. Estas construcciones deben estar en el nivel del lenguaje y deben escribirse a trav茅s del acceso de campo est谩tico, no de reflexi贸n.

Otro error de serializaci贸n es que est谩 intentando hacer demasiado. Se establece la tarea de poder serializar cualquier gr谩fico arbitrario de objetos (que puede contener bucles) y deserializarlo de nuevo sin romper su estado.

Este error se puede corregir simplificando la tarea y serializando no un gr谩fico de objetos, sino un 谩rbol de datos en el que no habr谩 un concepto de identidad (como en JSON).

驴C贸mo hacer que la serializaci贸n se ajuste naturalmente al modelo de objetos, use constructores para la deserializaci贸n, se separe del formato de codificaci贸n y admita el control de versiones? Con este fin, las anotaciones rescatan la posibilidad de un lenguaje que a煤n no est谩 incluido en Java: coincidencia de patrones . Por ejemplo:

public class Range { int lo; int hi; private Range(int lo, int hi) { if (lo > hi) throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); this.lo = lo; this.hi = hi; } @Serializer public pattern Range(int lo, int hi) { lo = this.lo; hi = this.hi; } @Deserializer public static Range make(int lo, int hi) { return new Range(lo, hi); } } 

En este ejemplo, se declara la clase Range, que est谩 lista para la serializaci贸n a trav茅s de dos miembros especiales de la clase: un serializador y un deserializador marcado con anotaciones @Serializer y @Deserializer. El serializador se implementa a trav茅s del deconstructor del patr贸n, y el deserializador se implementa a trav茅s del m茅todo est谩tico en el que se llama al constructor. Por lo tanto, durante la deserializaci贸n, inevitablemente se verifica el hi> = lo especificado en el constructor.
No hay magia en este enfoque, y se utilizan anotaciones regulares, por lo que cualquier marco puede hacer la serializaci贸n, y no solo la plataforma Java en s铆. Esto significa que el formato de codificaci贸n tambi茅n puede ser absolutamente cualquier cosa (binario, XML, JSON, YAML, etc.).

Dado que los serializadores y los deserializadores son m茅todos comunes, el programador tiene una gran libertad en su implementaci贸n. Por ejemplo, puede elegir una representaci贸n de un objeto diferente de la forma en que se representa el objeto en la memoria. Por ejemplo, LinkedList se puede serializar no en una cadena de enlaces, sino en una matriz continua, lo que har谩 que la presentaci贸n sea m谩s simple, m谩s eficiente y m谩s compacta.

La versi贸n en este enfoque se implementa utilizando el campo de versi贸n especial de las anotaciones @Serializer y @Deserializer:

 class C { int a; int b; int c; @Deserializer(version = 3) public C(int a, int b, int c) { this a = a; this.b = b; this.c = c; } @Deserializer(version = 2) public C(int a, int b) { this(a, b, 0); } @Deserializer(version = 1) public C(int a) { this(a, 0, 0); } @Serializer(version = 3) public pattern C(int a, int b, int c) { a = this.a; b = this.b; c = this.c; } } 

En este ejemplo, se llamar谩 a uno de los tres deserializadores, seg煤n la versi贸n.
驴Qu茅 sucede si no queremos que los serializadores y deserializadores est茅n disponibles para nadie m谩s que para fines de serializaci贸n? Para hacer esto, podemos hacerlos privados. Sin embargo, en este caso, un marco de serializaci贸n espec铆fico no podr谩 acceder a ellos a trav茅s de la reflexi贸n si dicho c贸digo est谩 dentro del m贸dulo en el que el paquete no est谩 abierto para un acceso reflexivo profundo. Para tal caso, se propone introducir otra nueva construcci贸n en el lenguaje: miembros de clase abierta. Por ejemplo:

 class Foo { private final InternalState is; public Foo(ExternalState es) { this(new InternalState(es)); } @Deserializer private open Foo(InternalState is) { this.is = is; } @Serializer private open pattern serialize(InternalState is) { is = this.is; } } 

Aqu铆, los serializadores y los deserializadores est谩n marcados con la palabra clave abierta, lo que los abre a setAccessible.

Por lo tanto, el nuevo enfoque es fundamentalmente diferente del anterior: en 茅l, las clases se dise帽an como serializables y no se dan a la plataforma tal como est谩n. Esto requiere un esfuerzo adicional, pero hace que la serializaci贸n sea m谩s predecible, m谩s segura e independiente del formato de codificaci贸n y el marco de serializaci贸n.

PD Amigos, si desean recibir noticias similares sobre Java de manera m谩s r谩pida y conveniente, suscr铆base a mi canal en Telegram.

Source: https://habr.com/ru/post/455880/


All Articles