Introdução ao Kryo
1. Visão geral
Kryo é uma estrutura de serialização Java com foco na velocidade, eficiência e uma API amigável.
Neste artigo, exploraremos os principais recursos da estrutura Kryo e implementaremos exemplos para mostrar seus recursos.
2. Dependência do Maven
A primeira coisa que precisamos fazer é adicionar a dependênciakryo ao nossopom.xml:
com.esotericsoftware
kryo
4.0.1
A versão mais recente deste artefato pode ser encontrada emMaven Central.
3. Kryo Basics
Vamos começar vendo como Kryo funciona e como podemos serializar e desserializar objetos com ele.
3.1. Introdução
A estrutura fornece a classeKryo como o principal ponto de entrada para todas as suas funcionalidades.
Esta classe orquestra o processo de serialização e mapeia as classes para instâncias deSerializer que lidam com os detalhes da conversão do gráfico de um objeto em uma representação de bytes.
Quando os bytes estão prontos, eles são gravados em um stream usando um objetoOutput. Dessa forma, eles podem ser armazenados em um arquivo, banco de dados ou transmitidos pela rede.
Posteriormente, quando o objeto for necessário, uma instânciaInput é usada para ler esses bytes e decodificá-los em objetos Java.
3.2. Serializando objetos
Antes de mergulhar nos exemplos, vamos primeiro criar um método utilitário para inicializar algumas variáveis que usaremos para cada caso de teste neste artigo:
@Before
public void init() {
kryo = new Kryo();
output = new Output(new FileOutputStream("file.dat"));
input = new Input(new FileInputStream("file.dat"));
}
Agora, podemos ver como é fácil escrever e ler um objeto usando o 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");
}
Observe a chamada ao métodoclose(). Isso é necessário porque as classesOutput eInput herdam deOutputStreameInputStream, respectivamente.
Serializar vários objetos é igualmente simples:
@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);
}
Observe que estamos passando a classe apropriada para o métodoreadObject(), isso torna nosso código livre de cast.
4. Serializadores
Nesta seção, mostraremos quaisSerializers já estão disponíveis e, em seguida, criaremos o nosso próprio.
4.1. Serializadores padrão
Quando Kryo serializa um objeto, ele cria uma instância de uma classeSerializer registrada anteriormente para fazer a conversão em bytes. Estes são chamados serializadores padrão e podem ser usados sem nenhuma configuração de nossa parte.
A biblioteca já fornece vários serializadores que processam primitivas, listas, mapas, enumerações, etc. Se nenhum serializador for encontrado para uma determinada classe, então umFieldSerializer é usado, o qual pode lidar com quase qualquer tipo de objeto.
Vamos ver como fica. Primeiro, vamos criar uma classePerson:
public class Person {
private String name = "John Doe";
private int age = 18;
private Date birthDate = new Date(933191282821L);
// standard constructors, getters, and setters
}
Agora, vamos escrever um objeto desta classe e depois lê-lo de volta:
@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");
}
Observe que não precisamos especificar nada para serializar um objetoPerson, pois umFieldSerializer é criado automaticamente para nós.
4.2. Serializadores personalizados
Se precisarmos de mais controle sobre o processo de serialização, temos duas opções; podemos escrever nossa própria classeSerializer e registrá-la com Kryo ou deixar que a classe faça a serialização sozinha.
Para demonstrar a primeira opção, vamos criar uma classe que estendaSerializer:
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;
}
}
Agora, vamos testar:
@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);
}
Observe que o campoage é igual a 18, embora o tenhamos definido anteriormente como 0.
Também podemos usar a anotação@DefaultSerializer para permitir que Kryo saiba que queremos usarPersonSerializer toda vez que ele precisar manipular um objetoPerson. Isso ajuda a evitar a chamada ao métodoregister():
@DefaultSerializer(PersonSerializer.class)
public class Person implements KryoSerializable {
// ...
}
Para a segunda opção, vamos modificar nossa classePerson para estender a interfaceKryoSerializable:
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();
// ...
}
}
Como o caso de teste para esta opção é igual ao anterior, não está incluído aqui. No entanto, você pode encontrá-lo no código fonte deste artigo.
4.3. Java Serializer
Em casos esporádicos, Kryo não será capaz de serializar uma classe. Se isso acontecer, e escrever um serializador personalizado não for uma opção, podemos usar o mecanismo de serialização Java padrão usando umJavaSerializer. Isso requer que a classe implemente a interfaceSerializable normalmente.
Aqui está um exemplo que usa o serializador mencionado acima:
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. Conclusão
Neste tutorial, exploramos os recursos mais notáveis da biblioteca Kryo.
Serializamos vários objetos simples e usamos a classeFieldSerializer para lidar com um personalizado. Também criamos um serializador personalizado e demonstramos como fazer o fallback para o mecanismo de serialização Java padrão, se necessário.
Como sempre, o código-fonte completo deste artigo pode ser encontradoover on Github.