Введение в Kryo

Введение в Крио

1. обзор

Kryo - это среда сериализации Java с упором на скорость, эффективность и удобный API.

В этой статье мы рассмотрим ключевые возможности платформы Kryo и реализуем примеры, чтобы продемонстрировать ее возможности.

2. Maven Dependency

Первое, что нам нужно сделать, это добавить зависимостьkryo к нашемуpom.xml:


    com.esotericsoftware
    kryo
    4.0.1

Последнюю версию этого артефакта можно найти наMaven Central.

3. Основы Kryo

Давайте начнем с того, что посмотрим, как работает Kryo и как с его помощью мы можем сериализовать и десериализовать объекты.

3.1. Вступление

Платформа предоставляет классKryo в качестве основной точки входа для всех своих функций.

Этот класс управляет процессом сериализации и сопоставляет классы с экземплярамиSerializer, которые обрабатывают детали преобразования графа объекта в байтовое представление.

Когда байты готовы, они записываются в поток с помощью объектаOutput. Таким образом, они могут храниться в файле, базе данных или передаваться по сети.

Позже, когда объект понадобится, экземплярInput используется для чтения этих байтов и декодирования их в объекты Java.

3.2. Сериализация объектов

Прежде чем погрузиться в примеры, давайте сначала создадим служебный метод для инициализации некоторых переменных, которые мы будем использовать для каждого тестового примера в этой статье:

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

Теперь мы можем посмотреть, как легко написать и прочитать объект, используя 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");
}

Обратите внимание на вызов методаclose(). Это необходимо, поскольку классыOutput иInput наследуются отOutputStream иInputStream соответственно.

Сериализация нескольких объектов также проста:

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

Обратите внимание, что мы передаем соответствующий класс методуreadObject(), это делает наш код свободным от приведений.

4. сериализаторы

В этом разделе мы покажем, какиеSerializers уже доступны, а затем создадим свои собственные.

4.1. Сериализаторы по умолчанию

Когда Kryo сериализует объект, он создает экземпляр ранее зарегистрированного классаSerializer для преобразования в байты. Они называются сериализаторами по умолчанию и могут использоваться без какой-либо настройки с нашей стороны.

Библиотека уже предоставляет несколько таких сериализаторов, которые обрабатывают примитивы, списки, карты, перечисления и т. Д. Если для данного класса не найден сериализатор, то используетсяFieldSerializer, который может обрабатывать практически любой тип объекта.

Посмотрим, как это выглядит. Во-первых, давайте создадим классPerson:

public class Person {
    private String name = "John Doe";
    private int age = 18;
    private Date birthDate = new Date(933191282821L);

    // standard constructors, getters, and setters
}

Теперь давайте напишем объект из этого класса, а затем прочитаем его обратно:

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

Обратите внимание, что нам не нужно было ничего указывать для сериализации объектаPerson, посколькуFieldSerializer создается для нас автоматически.

4.2. Пользовательские сериализаторы

Если нам нужно больше контроля над процессом сериализации, у нас есть два варианта; мы можем написать наш собственный классSerializer и зарегистрировать его в Kryo или позволить классу обрабатывать сериализацию самостоятельно.

Чтобы продемонстрировать первый вариант, давайте создадим класс, расширяющий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;
    }
}

А теперь давайте проверим:

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

Обратите внимание, что полеage равно 18, хотя мы ранее установили его в 0.

Мы также можем использовать аннотацию@DefaultSerializer, чтобы сообщить Kryo, что мы хотим использоватьPersonSerializer каждый раз, когда ей нужно обрабатывать объектPerson. Это помогает избежать вызова методаregister():

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

Для второго варианта давайте изменим наш классPerson, чтобы расширить интерфейс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();
        // ...
    }
}

Поскольку контрольный пример для этой опции равен предыдущему, сюда не включается. Тем не менее, вы можете найти его в исходном коде для этой статьи.

4.3. Java-сериализатор

В отдельных случаях Kryo не сможет сериализовать класс. Если это произойдет и написать собственный сериализатор невозможно, мы можем использовать стандартный механизм сериализации Java с использованиемJavaSerializer. Для этого требуется, чтобы класс реализовал интерфейсSerializable как обычно.

Вот пример, в котором используется вышеупомянутый сериализатор:

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. Заключение

В этом уроке мы рассмотрели наиболее заметные особенности библиотеки Kryo.

Мы сериализовали несколько простых объектов и использовали классFieldSerializer для работы с настраиваемым. Мы также создали собственный сериализатор и продемонстрировали, как при необходимости вернуться к стандартному механизму сериализации Java.

Как всегда, полный исходный код этой статьи можно найти наover on Github.