Einführung in Kryo

1. Übersicht

Kryo ist ein Java-Serialisierungsframework mit Fokus auf Geschwindigkeit, Effizienz und einer benutzerfreundlichen API.

In diesem Artikel werden wir die wichtigsten Funktionen des Kryo-Frameworks untersuchen und Beispiele implementieren, um seine Funktionen zu demonstrieren.

2. Maven-Abhängigkeit

Das erste, was wir tun müssen, ist, die Kryo- Abhängigkeit zu unserer pom.xml hinzuzufügen :

 com.esotericsoftware kryo 4.0.1 

Die neueste Version dieses Artefakts finden Sie auf Maven Central.

3. Kryo-Grundlagen

Schauen wir uns zunächst an, wie Kryo funktioniert und wie wir damit Objekte serialisieren und deserialisieren können.

3.1. Einführung

Das Framework bietet die Kryo- Klasse als Haupteinstiegspunkt für alle ihre Funktionen.

Diese Klasse koordiniert den Serialisierungsprozess und ordnet Klassen Serializer- Instanzen zu, die die Details der Konvertierung des Diagramms eines Objekts in eine Byte-Darstellung verarbeiten.

Sobald die Bytes fertig sind, werden sie mithilfe eines Ausgabeobjekts in einen Stream geschrieben . Auf diese Weise können sie in einer Datei, einer Datenbank gespeichert oder über das Netzwerk übertragen werden.

Später, wenn das Objekt benötigt wird, wird eine Eingabeinstanz verwendet, um diese Bytes zu lesen und in Java-Objekte zu dekodieren.

3.2. Objekte serialisieren

Bevor wir uns mit Beispielen befassen, erstellen wir zunächst eine Dienstprogrammmethode, um einige Variablen zu initialisieren, die wir für jeden Testfall in diesem Artikel verwenden:

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

Jetzt können wir sehen, wie einfach es ist, ein Objekt mit Kryo zu schreiben und zu lesen:

@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"); }

Beachten Sie den Aufruf der Methode close () . Dies ist erforderlich, da die Klassen Output und Input von OutputStream bzw. InputStream erben .

Das Serialisieren mehrerer Objekte ist ähnlich einfach:

@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); }

Beachten Sie, dass wir die entsprechende Klasse an die readObject () -Methode übergeben. Dadurch wird unser Code umwandlungsfrei .

4. Serializer

In diesem Abschnitt zeigen wir, welche Serializer bereits verfügbar sind, und erstellen dann unsere eigenen.

4.1. Standard-Serializer

Wenn Kryo ein Objekt serialisiert, erstellt es eine Instanz einer zuvor registrierten Serializer- Klasse, um die Konvertierung in Bytes durchzuführen. Diese werden als Standard-Serializer bezeichnet und können ohne unser Zutun verwendet werden.

Die Bibliothek bietet bereits mehrere solcher Serializer, die Grundelemente, Listen, Karten, Aufzählungen usw. verarbeiten. Wenn für eine bestimmte Klasse kein Serializer gefunden wird, wird ein FieldSerializer verwendet, der nahezu jeden Objekttyp verarbeiten kann.

Mal sehen, wie das aussieht. Lassen Sie uns zunächst eine Personenklasse erstellen :

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

Schreiben wir nun ein Objekt aus dieser Klasse und lesen es dann zurück:

@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"); }

Beachten Sie, dass wir nichts angeben mussten, um ein Personenobjekt zu serialisieren, da ein FieldSerializer automatisch für uns erstellt wird.

4.2. Benutzerdefinierte Serializer

Wenn wir mehr Kontrolle über den Serialisierungsprozess benötigen, haben wir zwei Möglichkeiten. Wir können unsere eigene Serializer- Klasse schreiben und bei Kryo registrieren oder die Klasse die Serialisierung selbst durchführen lassen.

Um die erste Option zu demonstrieren, erstellen wir eine Klasse, die Serializer erweitert :

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; } }

Lassen Sie es uns jetzt testen:

@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); }

Beachten Sie, dass das Altersfeld gleich 18 ist, obwohl wir es zuvor auf 0 gesetzt haben.

Wir können auch die Annotation @DefaultSerializer verwenden , um Kryo mitzuteilen , dass wir den PersonSerializer jedes Mal verwenden möchten, wenn er ein Person- Objekt verarbeiten muss. Dies hilft, den Aufruf der register () -Methode zu vermeiden :

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

Für die zweite Option ändern wir unsere Person- Klasse, um die KryoSerializable- Schnittstelle zu erweitern:

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(); // ... } }

Da der Testfall für diese Option einem vorherigen entspricht, wird er hier nicht berücksichtigt. Sie finden es jedoch im Quellcode dieses Artikels.

4.3. Java Serializer

In sporadischen Fällen kann Kryo eine Klasse nicht serialisieren. Wenn dies passiert und das Schreiben eines benutzerdefinierten Serializers keine Option ist, können wir den Standardmechanismus für die Java-Serialisierung mithilfe eines JavaSerializers verwenden . Dies erfordert, dass die Klasse die serialisierbare Schnittstelle wie gewohnt implementiert .

Hier ist ein Beispiel, das den oben genannten Serializer verwendet:

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. Schlussfolgerung

In diesem Tutorial haben wir die bemerkenswertesten Funktionen der Kryo-Bibliothek untersucht.

Wir haben mehrere einfache Objekte serialisiert und die FieldSerializer- Klasse verwendet, um mit einem benutzerdefinierten Objekt umzugehen. Wir haben auch einen benutzerdefinierten Serializer erstellt und gezeigt, wie bei Bedarf auf den Standard-Java-Serialisierungsmechanismus zurückgegriffen werden kann.

Wie immer finden Sie den vollständigen Quellcode für diesen Artikel auf Github.