Einführung in Kryo

Einführung in Kryo

1. Überblick

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

In diesem Artikel werden die wichtigsten Funktionen des Kryo-Frameworks erläutert und Beispiele implementiert, um seine Funktionen zu demonstrieren.

2. Maven-Abhängigkeit

Als erstes müssen wir die Abhängigkeit vonkryozu unserenpom.xmlhinzufügen:


    com.esotericsoftware
    kryo
    4.0.1

Die neueste Version dieses Artefakts befindet sich aufMaven 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 KlasseKryoals Haupteinstiegspunkt für alle Funktionen.

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

Sobald die Bytes fertig sind, werden sie mit einemOutput-Objekt 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 eineInput-Instanz 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 zum Initialisieren einiger Variablen, 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, mit Kryo ein Objekt 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 Methodeclose(). Dies ist erforderlich, da die KlassenOutput undInput vonOutputStream 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 diereadObject()-Methode übergeben. Dadurch wird unser Code umwandlungsfrei.

4. Serialisierer

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

4.1. Standard-Serializer

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

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

Mal sehen, wie das aussieht. Lassen Sie uns zunächst einePerson-Klasse 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 einPerson-Objekt zu serialisieren, da einFieldSerializer 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 eigeneSerializer-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, dieSerializer 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 Feldagegleich 18 ist, obwohl wir es zuvor auf 0 gesetzt haben.

Wir können auch die Annotation@DefaultSerializer verwenden, um Kryo mitzuteilen, dass wir diePersonSerializer jedes Mal verwenden möchten, wenn einPerson-Objekt verarbeitet werden soll. Dies hilft, den Aufruf derregister()-Methode zu vermeiden:

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

Für die zweite Option ändern wir die KlassePerson, um die SchnittstelleKryoSerializablezu 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 gleich einem vorherigen ist, wird 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 Standard-Java-Serialisierungsmechanismus mitJavaSerializer verwenden. Dies erfordert, dass die Klasse dieSerializable-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. Fazit

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

Wir haben mehrere einfache Objekte serialisiert und die KlasseFieldSerializerverwendet, um mit einem benutzerdefinierten Objekt umzugehen. Wir haben auch einen benutzerdefinierten Serializer erstellt und gezeigt, wie bei Bedarf auf den standardmäßigen Java-Serialisierungsmechanismus zurückgegriffen werden kann.

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