1.概述
Kryo是一個(gè)Java序列化框架欺嗤。本文將嘗試著探索Kryo框架的關(guān)鍵功能,并用示例展示出來(lái)。
2. Maven依賴
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.1</version>
</dependency>
可以在Maven Central上找到最新版本圃阳。
3. Kryo基礎(chǔ)
Kryo的工作方式孽文,以及如何使用它來(lái)序列化和反序列化對(duì)象驻襟。
3.1 介紹
該框架將Kryo類作為其所有功能的主要入口點(diǎn)。
此類協(xié)調(diào)序列化過(guò)程芋哭,并將將對(duì)象轉(zhuǎn)換為字節(jié)形式沉衣。字節(jié)準(zhǔn)備好后,將使用Output對(duì)象將它們寫入流减牺。這樣豌习,它們可以存儲(chǔ)在文件,數(shù)據(jù)庫(kù)中或通過(guò)網(wǎng)絡(luò)傳輸拔疚。稍后肥隆,當(dāng)需要該對(duì)象時(shí),將使用Input實(shí)例讀取這些字節(jié)并將其解碼為Java對(duì)象稚失。
3.2 序列化對(duì)象
首先初始化測(cè)試用例的一些變量:
@Before
public void init() {
kryo = new Kryo();
output = new Output(new FileOutputStream("file.dat"));
input = new Input(new FileInputStream("file.dat"));
}
使用Kryo寫和讀對(duì)象:
@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");
}
請(qǐng)注意對(duì)close()方法的調(diào)用栋艳。這是必需的,因?yàn)?em>Output和Input類分別繼承自OutputStream和InputStream墩虹。
序列化多個(gè)對(duì)象同樣簡(jiǎn)單:
@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);
}
請(qǐng)注意嘱巾,我們正在將適當(dāng)?shù)念悅鬟f給readObject()方法憨琳,這使我們的代碼無(wú)需轉(zhuǎn)換。
4.序列化器
在本節(jié)中旬昭,我們將顯示哪些序列化器已經(jīng)可用篙螟,然后我們將創(chuàng)建自己的序列化器。
4.1 默認(rèn)序列化器
當(dāng)Kryo序列化一個(gè)對(duì)象時(shí)问拘,它將創(chuàng)建一個(gè)先前注冊(cè)的Serializer類的實(shí)例遍略,以將其轉(zhuǎn)換為字節(jié)。這些稱為默認(rèn)序列化器骤坐,無(wú)需我們?nèi)魏卧O(shè)置即可使用绪杏。
該庫(kù)已經(jīng)提供了幾個(gè)處理列表,映射纽绍,枚舉等的序列化器蕾久。如果找不到給定類的序列化器,則使用FieldSerializer拌夏,它可以處理幾乎任何類型的對(duì)象僧著。
讓我們看看它的樣子。首先障簿,讓我們創(chuàng)建一個(gè)Person類:
public class Person {
private String name = "John Doe";
private int age = 18;
private Date birthDate = new Date(933191282821L);
// standard constructors, getters, and setters
}
現(xiàn)在盹愚,讓我們從此類中編寫一個(gè)對(duì)象,然后將其讀回:
@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");
}
注意站故,由于FieldSerializer是為我們自動(dòng)創(chuàng)建的皆怕,因此無(wú)需指定任何內(nèi)容來(lái)序列化Person對(duì)象。
4.2 自定義序列化器
如果需要對(duì)序列化過(guò)程進(jìn)行更多控制西篓,則有兩個(gè)選擇愈腾;我們可以編寫自己的Serializer類,并在Kryo中注冊(cè)它污淋,或者讓該類自行處理序列化顶滩。
為了演示第一個(gè)選項(xiàng),讓我們創(chuàng)建一個(gè)擴(kuò)展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;
}
}
現(xiàn)在寸爆,讓我們對(duì)其進(jìn)行測(cè)試:
@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);
}
請(qǐng)注意,即使我們之前將年齡字段設(shè)置為0盐欺,年齡字段也等于18赁豆。
我們還可以使用@DefaultSerializer注釋讓Kryo知道我們每次需要處理Person對(duì)象時(shí)都想使用PersonSerializer。這有助于避免調(diào)用register()方法:
@DefaultSerializer(PersonSerializer.class)
public class Person implements KryoSerializable {
// ...
}
對(duì)于第二個(gè)選項(xiàng)冗美,讓我們修改Person類以擴(kuò)展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();
// ...
}
}
由于此選項(xiàng)的測(cè)試用例等于前一個(gè)魔种,因此此處不包括。但是粉洼,您可以在本文的源代碼中找到它节预。
4.3 Java序列化器
在零星的情況下叶摄,Kryo將無(wú)法序列化一個(gè)類。如果發(fā)生這種情況安拟,并且不能編寫自定義序列化程序蛤吓,則可以使用JavaSerializer使用標(biāo)準(zhǔn)的Java序列化機(jī)制。這就要求該類照常實(shí)現(xiàn)Serializable接口糠赦。
這是一個(gè)使用上述序列化器的示例:
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.結(jié)論
以上探索了Kryo庫(kù)最關(guān)鍵的功能会傲。序列化了多個(gè)簡(jiǎn)單對(duì)象,并使用FieldSerializer類處理自定義對(duì)象拙泽。我們還創(chuàng)建了一個(gè)自定義序列化程序淌山,并演示了如何在需要時(shí)退回到標(biāo)準(zhǔn)Java序列化機(jī)制。