1. Overview
1.概述
Kryo is a Java serialization framework with a focus on speed, efficiency, and a user-friendly API.
Kryo是一个Java序列化框架,重点在于速度、效率和用户友好的API。
In this article, we’ll explore the key features of the Kryo framework and implement examples to showcase its capabilities.
在这篇文章中,我们将探讨Kryo框架的主要特征,并实施实例来展示其能力。
2. Maven Dependency
2.Maven的依赖性
The first thing we need to do is to add the kryo dependency to our pom.xml:
我们需要做的第一件事是将kryo依赖性添加到我们的pom.xml。
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.1</version>
</dependency>
The latest version of this artifact can be found on Maven Central.
该工件的最新版本可以在Maven Central上找到。
3. Kryo Basics
3.Kryo基础知识
Let’s start by looking at how Kryo works and how we can serialize and deserialize objects with it.
让我们先来看看Kryo是如何工作的,以及我们如何用它来序列化和反序列化对象。
3.1. Introduction
3.1.简介
The framework provides the Kryo class as the main entry point for all its functionality.
该框架提供了Kryo类作为其所有功能的主要入口点。
This class orchestrates the serialization process and maps classes to Serializer instances which handle the details of converting an object’s graph to a byte representation.
这个类协调了序列化过程,并将类映射到Serializer实例中,这些实例处理将对象的图形转换为字节表示的细节。
Once the bytes are ready, they’re written to a stream using an Output object. This way they can be stored in a file, a database or transmitted over the network.
一旦字节准备好了,就用Output对象将它们写入一个流。这样,它们就可以被存储在文件、数据库或通过网络传输。
Later, when the object is needed, an Input instance is used to read those bytes and decode them into Java objects.
后来,当需要该对象时,一个Input实例被用来读取这些字节并将其解码为Java对象。
3.2. Serializing Objects
3.2.序列化对象
Before diving into examples, let’s first create a utility method to initialize some variables we’ll use for each test case in this article:
在深入研究例子之前,让我们首先创建一个实用方法来初始化一些变量,我们将在本文的每个测试案例中使用。
@Before
public void init() {
kryo = new Kryo();
output = new Output(new FileOutputStream("file.dat"));
input = new Input(new FileInputStream("file.dat"));
}
Now, we can look a how easy is to write and read an object using Kryo:
现在,我们可以看看使用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");
}
Notice the call to the close() method. This is needed since Output and Input classes inherit from OutputStream and InputStream respectively.
注意对close()方法的调用。这是需要的,因为Output和Input类分别继承于OutputStream和InputStream。
Serializing multiple objects is similarly straightforward:
对多个对象进行序列化也同样简单明了。
@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);
}
Notice that we’re passing the appropriate class to the readObject() method, this makes our code cast-free.
注意,我们把适当的类传递给了readObject()方法,这使得我们的代码没有投射。
4. Serializers
4.序列化器
In this section, we’ll show which Serializers are already available, and then we’ll create our own.
在本节中,我们将展示哪些序列化器已经可用,然后我们将创建我们自己的序列化器。
4.1. Default Serializers
4.1.默认的序列化器
When Kryo serializes an object, it creates an instance of a previously registered Serializer class to do the conversion to bytes. These are called default serializers and can be used without any setup on our part.
当Kryo序列化一个对象时,它会创建一个先前注册的Serializer类的实例来完成向字节的转换。这些被称为默认的序列化器,无需我们进行任何设置就可以使用。
The library already provides several such serializers that process primitives, lists, maps, enums, etc. If no serializer is found for a given class, then a FieldSerializer is used, which can handle almost any type of object.
该库已经提供了几个这样的序列化器,可以处理基元、列表、地图、枚举等。如果没有为一个给定的类找到序列化器,那么就会使用FieldSerializer,它可以处理几乎所有类型的对象。
Let’s see how this looks like. First, lets’ create a Person class:
让我们看看这看起来像什么。首先,让我们创建一个Person类。
public class Person {
private String name = "John Doe";
private int age = 18;
private Date birthDate = new Date(933191282821L);
// standard constructors, getters, and setters
}
Now, let’s write an object from this class and then read it back:
现在,让我们从这个类中写一个对象,然后再读回来。
@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");
}
Notice that we didn’t have to specify anything to serialize a Person object since a FieldSerializer is created automatically for us.
请注意,我们不必指定任何东西来序列化一个Person对象,因为一个FieldSerializer已经为我们自动创建。
4.2. Custom Serializers
4.2.自定义序列化器
If we need more control over the serialization process, we have two options; we can write our own Serializer class and register it with Kryo or let the class handle the serialization by itself.
如果我们需要对序列化过程进行更多的控制,我们有两个选择;我们可以编写我们自己的Serializer类并在Kryo注册,或者让这个类自己处理序列化。
To demonstrate the first option, let’s create a class that extends Serializer:
为了演示第一个方案,让我们创建一个扩展Serializer的类。
public class PersonSerializer extends Serializer<Person> {
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<Person> 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;
}
}
Now, let’s put it to test:
现在,让我们来测试一下。
@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);
}
Notice that the age field is equal to 18, even though we set it previously to 0.
请注意,年龄字段等于18,尽管我们之前将其设置为0。
We can also use the @DefaultSerializer annotation to let Kryo know we want to use the PersonSerializer each time it needs to handle a Person object. This helps avoid the call to the register() method:
我们还可以使用@DefaultSerializer注解来让Kryo知道我们想在每次需要处理Person对象时使用PersonSerializer。这有助于避免对register()方法的调用。
@DefaultSerializer(PersonSerializer.class)
public class Person implements KryoSerializable {
// ...
}
For the second option, let’s modify our Person class to extend the KryoSerializable interface:
对于第二个选择,让我们修改我们的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();
// ...
}
}
Since the test case for this option is equal to a previous one, is not included here. However, you can find it in the source code for this article.
由于这个选项的测试案例与之前的案例相同,所以这里不包括。然而,你可以在本文的源代码中找到它。
4.3. Java Serializer
4.3.Java串行器
In sporadic cases, Kryo won’t be able to serialize a class. If this happens, and writing a custom serializer isn’t an option, we can use the standard Java serialization mechanism using a JavaSerializer. This requires that the class implements the Serializable interface as usual.
在零星的情况下,Kryo将无法序列化一个类。如果发生这种情况,并且编写一个自定义的序列化器不是一个选项,我们可以使用JavaSerializer的标准Java序列化机制。这需要该类像往常一样实现Serializable接口。
Here’s an example that uses the aforementioned serializer:
下面是一个使用上述串行器的例子。
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. Conclusion
5.结论
In this tutorial, we explored the most notable features of the Kryo library.
在本教程中,我们探讨了Kryo库的最显著的特点。
We serialized multiple simple objects and used the FieldSerializer class to deal with a custom one. We also created a custom serializer and demonstrated how to fallback to the standard Java serialization mechanism if needed.
我们序列化了多个简单的对象,并使用FieldSerializer类来处理一个自定义的对象。我们还创建了一个自定义的序列化器,并演示了如何在需要时回退到标准的Java序列化机制。
As always, the complete source code for this article can be found over on Github.
一如既往,本文的完整源代码可以在Github上找到over。