Introduzione a Kryo

1. Panoramica

Kryo è un framework di serializzazione Java incentrato su velocità, efficienza e un'API user-friendly.

In questo articolo, esploreremo le caratteristiche chiave del framework Kryo e implementeremo esempi per mostrare le sue capacità.

2. Dipendenza da Maven

La prima cosa che dobbiamo fare è aggiungere la dipendenza kryo al nostro pom.xml :

 com.esotericsoftware kryo 4.0.1 

L'ultima versione di questo artefatto può essere trovata su Maven Central.

3. Fondamenti di Kryo

Cominciamo osservando come funziona Kryo e come possiamo serializzare e deserializzare gli oggetti con esso.

3.1. introduzione

Il framework fornisce la classe Kryo come punto di ingresso principale per tutte le sue funzionalità.

Questa classe orchestra il processo di serializzazione e mappa le classi alle istanze di Serializer che gestiscono i dettagli della conversione del grafico di un oggetto in una rappresentazione in byte.

Una volta che i byte sono pronti, vengono scritti in un flusso utilizzando un oggetto Output . In questo modo possono essere archiviati in un file, un database o trasmessi in rete.

Successivamente, quando l'oggetto è necessario, viene utilizzata un'istanza di Input per leggere quei byte e decodificarli in oggetti Java.

3.2. Serializzazione di oggetti

Prima di immergerci negli esempi, creiamo prima un metodo di utilità per inizializzare alcune variabili che useremo per ogni caso di test in questo articolo:

@Before public void init() { kryo = new Kryo(); output = new Output(new FileOutputStream("file.dat")); input = new Input(new FileInputStream("file.dat")); }

Ora possiamo vedere quanto è facile scrivere e leggere un oggetto usando Kryo:

@Test public void givenObject_whenSerializing_thenReadCorrectly() { Object someObject = "Some string"; kryo.writeClassAndObject(output, someObject); output.close(); Object theObject = kryo.readClassAndObject(input); input.close(); assertEquals(theObject, "Some string"); }

Notare la chiamata al metodo close () . Ciò è necessario poiché le classi Output e Input ereditano rispettivamente da OutputStream e InputStream .

Serializzare più oggetti è altrettanto semplice:

@Test public void givenObjects_whenSerializing_thenReadCorrectly() { String someString = "Multiple Objects"; Date someDate = new Date(915170400000L); kryo.writeObject(output, someString); kryo.writeObject(output, someDate); output.close(); String readString = kryo.readObject(input, String.class); Date readDate = kryo.readObject(input, Date.class); input.close(); assertEquals(readString, "Multiple Objects"); assertEquals(readDate.getTime(), 915170400000L); }

Si noti che stiamo passando la classe appropriata al metodo readObject () , questo rende il nostro codice privo di cast.

4. Serializzatori

In questa sezione, mostreremo quali Serializer sono già disponibili, quindi ne creeremo uno nostro.

4.1. Serializzatori predefiniti

Quando Kryo serializza un oggetto, crea un'istanza di una classe Serializer registrata in precedenza per eseguire la conversione in byte. Questi sono chiamati serializzatori di default e possono essere utilizzati senza alcuna configurazione da parte nostra.

La libreria fornisce già diversi serializzatori di questo tipo che elaborano primitive, elenchi, mappe, enumerazioni, ecc. Se non viene trovato alcun serializzatore per una data classe, viene utilizzato un FieldSerializer , che può gestire quasi ogni tipo di oggetto.

Vediamo come appare. Innanzitutto, creiamo una classe Person :

public class Person { private String name = "John Doe"; private int age = 18; private Date birthDate = new Date(933191282821L); // standard constructors, getters, and setters }

Ora scriviamo un oggetto da questa classe e poi leggiamolo di nuovo:

@Test public void givenPerson_whenSerializing_thenReadCorrectly() { Person person = new Person(); kryo.writeObject(output, person); output.close(); Person readPerson = kryo.readObject(input, Person.class); input.close(); assertEquals(readPerson.getName(), "John Doe"); }

Si noti che non è stato necessario specificare nulla per serializzare un oggetto Person poiché un FieldSerializer viene creato automaticamente per noi.

4.2. Serializzatori personalizzati

Se abbiamo bisogno di un maggiore controllo sul processo di serializzazione, abbiamo due opzioni; possiamo scrivere la nostra classe Serializer e registrarla con Kryo o lasciare che la classe gestisca la serializzazione da sola.

Per dimostrare la prima opzione, creiamo una classe che estende Serializer :

public class PersonSerializer extends Serializer { public void write(Kryo kryo, Output output, Person object) { output.writeString(object.getName()); output.writeLong(object.getBirthDate().getTime()); } public Person read(Kryo kryo, Input input, Class type) { Person person = new Person(); person.setName(input.readString()); long birthDate = input.readLong(); person.setBirthDate(new Date(birthDate)); person.setAge(calculateAge(birthDate)); return person; } private int calculateAge(long birthDate) { // Some custom logic return 18; } }

Ora, mettiamolo alla prova:

@Test public void givenPerson_whenUsingCustomSerializer_thenReadCorrectly() { Person person = new Person(); person.setAge(0); kryo.register(Person.class, new PersonSerializer()); kryo.writeObject(output, person); output.close(); Person readPerson = kryo.readObject(input, Person.class); input.close(); assertEquals(readPerson.getName(), "John Doe"); assertEquals(readPerson.getAge(), 18); }

Nota che il campo età è uguale a 18, anche se lo abbiamo impostato in precedenza su 0.

Possiamo anche usare l' annotazione @DefaultSerializer per far sapere a Kryo che vogliamo usare PersonSerializer ogni volta che deve gestire un oggetto Person . Questo aiuta a evitare la chiamata al metodo register () :

@DefaultSerializer(PersonSerializer.class) public class Person implements KryoSerializable { // ... }

Per la seconda opzione, modifichiamo la nostra classe Person per estendere l' interfaccia KryoSerializable :

public class Person implements KryoSerializable { // ... public void write(Kryo kryo, Output output) { output.writeString(name); // ... } public void read(Kryo kryo, Input input) { name = input.readString(); // ... } }

Poiché il test case per questa opzione è uguale a quello precedente, non è incluso qui. Tuttavia, puoi trovarlo nel codice sorgente di questo articolo.

4.3. Serializzatore Java

In casi sporadici, Kryo non sarà in grado di serializzare una classe. Se ciò accade e la scrittura di un serializzatore personalizzato non è un'opzione, possiamo utilizzare il meccanismo di serializzazione Java standard utilizzando un JavaSerializer . Ciò richiede che la classe implementi l' interfaccia Serializable come al solito.

Ecco un esempio che utilizza il suddetto serializzatore:

public class ComplexObject implements Serializable { private String name = "Bael"; // standard getters and setters }
@Test public void givenJavaSerializable_whenSerializing_thenReadCorrectly() { ComplexClass complexObject = new ComplexClass(); kryo.register(ComplexClass.class, new JavaSerializer()); kryo.writeObject(output, complexObject); output.close(); ComplexClass readComplexObject = kryo.readObject(input, ComplexClass.class); input.close(); assertEquals(readComplexObject.getName(), "Bael"); }

5. conclusione

In questo tutorial, abbiamo esplorato le caratteristiche più importanti della libreria Kryo.

Abbiamo serializzato più oggetti semplici e utilizzato la classe FieldSerializer per gestirne uno personalizzato. Abbiamo anche creato un serializzatore personalizzato e dimostrato come eseguire il fallback al meccanismo di serializzazione Java standard, se necessario.

Come sempre, il codice sorgente completo di questo articolo può essere trovato su Github.