Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[javalib-io-binary] 二进制序列化/反序列化工具示例
  • Loading branch information
dunwu committed Nov 26, 2019
commit bd88a390e1ac8bf8b080e01dad9550c8da70675b
4 changes: 0 additions & 4 deletions javalib-io-binary/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@
<version>5.0.0-RC4</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.github.dunwu</groupId>
<artifactId>dunwu-common</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import org.nustaq.serialization.FSTConfiguration;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
* fast-serialization lib Demo
* <a href="https://github.com/RuedigerMoeller/fast-serialization">FST</a> 序列化/反序列化示例
*
* @author <a href="mailto:forbreak@163.com">Zhang Peng</a>
* @see <a href="https://github.com/RuedigerMoeller/fast-serialization">FST</a>
Expand All @@ -15,11 +17,38 @@ public class FstDemo {

private static FSTConfiguration DEFAULT_CONFIG = FSTConfiguration.createDefaultConfiguration();

public static <T> byte[] serialize(T obj) {
/**
* 将对象序列化为 byte 数组
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的 byte 数组
*/
public static <T> byte[] writeToBytes(T obj) {
return DEFAULT_CONFIG.asByteArray(obj);
}

public static <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException {
/**
* 将对象序列化为 byte 数组后,再使用 Base64 编码
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的字符串
*/
public static <T> String writeToString(T obj) {
byte[] bytes = writeToBytes(obj);
return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
}

/**
* 将 byte 数组反序列化为原对象
*
* @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
public static <T> T readFromBytes(byte[] bytes, Class<T> clazz) throws IOException {
Object obj = DEFAULT_CONFIG.asObject(bytes);
if (clazz.isInstance(obj)) {
return (T) obj;
Expand All @@ -28,4 +57,17 @@ public static <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException
}
}

/**
* 将字符串反序列化为原对象,先使用 Base64 解码
*
* @param str {@link #writeToString} 方法序列化后的字符串
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
public static <T> T readFromString(String str, Class<T> clazz) throws IOException {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
return readFromBytes(Base64.getDecoder().decode(bytes), clazz);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.github.dunwu.javalib.io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
* JDK 默认序列化、反序列化机制示例
Expand All @@ -10,7 +12,14 @@
*/
public class JdkSerializeDemo {

public static <T> byte[] serialize(T obj) throws IOException {
/**
* 将对象序列化为 byte 数组
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的 byte 数组
*/
public static <T> byte[] writeToBytes(T obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
Expand All @@ -20,7 +29,27 @@ public static <T> byte[] serialize(T obj) throws IOException {
return bytes;
}

public static <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException, ClassNotFoundException {
/**
* 将对象序列化为 byte 数组后,再使用 Base64 编码
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的字符串
*/
public static <T> String writeToString(T obj) throws IOException {
byte[] bytes = writeToBytes(obj);
return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
}

/**
* 将 byte 数组反序列化为原对象
*
* @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
public static <T> T readFromBytes(byte[] bytes, Class<T> clazz) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
Object obj = ois.readObject();
Expand All @@ -33,4 +62,17 @@ public static <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException
}
}

/**
* 将字符串反序列化为原对象,先使用 Base64 解码
*
* @param str {@link #writeToString} 方法序列化后的字符串
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
public static <T> T readFromString(String str, Class<T> clazz) throws IOException, ClassNotFoundException {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
return readFromBytes(Base64.getDecoder().decode(bytes), clazz);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package io.github.dunwu.javalib.io;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy;
import org.objenesis.strategy.StdInstantiatorStrategy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
* <a href="https://github.com/EsotericSoftware/kryo">Kyro</a> 序列化/反序列化示例
*
* @author <a href="mailto:forbreak@163.com">Zhang Peng</a>
* @author <a href="https://www.cnblogs.com/hntyzgn/p/7122709.html">Kryo 使用指南</a>
* @since 2019-11-26
*/
public class KryoDemo {

// 每个线程的 Kryo 实例
private static final ThreadLocal<Kryo> kryoLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();

/**
* 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化,
* 上线的同时就必须清除 Redis 里的所有缓存,
* 否则那些缓存再回来反序列化的时候,就会报错
*/
//支持对象循环引用(否则会栈溢出)
kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置

//不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置

//Fix the NPE bug when deserializing Collections.
((DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());

return kryo;
});

/**
* 获得当前线程的 Kryo 实例
*
* @return 当前线程的 Kryo 实例
*/
public static Kryo getInstance() {
return kryoLocal.get();
}

/**
* 将对象序列化为 byte 数组
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的 byte 数组
*/
public static <T> byte[] writeToBytes(T obj) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);

Kryo kryo = getInstance();
kryo.writeObject(output, obj);
output.flush();

return byteArrayOutputStream.toByteArray();
}

/**
* 将对象序列化为 byte 数组后,再使用 Base64 编码
*
* @param obj 任意对象
* @param <T> 对象的类型
* @return 序列化后的字符串
*/
public static <T> String writeToString(T obj) {
byte[] bytes = writeToBytes(obj);
return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
}

/**
* 将 byte 数组反序列化为原对象
*
* @param bytes {@link #writeToBytes} 方法序列化后的 byte 数组
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
@SuppressWarnings("unchecked")
public static <T> T readFromBytes(byte[] bytes, Class<T> clazz) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);

Kryo kryo = getInstance();
return (T) kryo.readObject(input, clazz);
}

/**
* 将字符串反序列化为原对象,先使用 Base64 解码
*
* @param str {@link #writeToString} 方法序列化后的字符串
* @param clazz 原对象的类型
* @param <T> 原对象的类型
* @return 原对象
*/
public static <T> T readFromString(String str, Class<T> clazz) {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
return readFromBytes(Base64.getDecoder().decode(bytes), clazz);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.dunwu.javalib.io.bean;

import java.sql.Date;
import io.github.dunwu.util.time.DateExtUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
Expand All @@ -14,15 +15,14 @@ public class BeanUtils {
public static TestBean initJdk8Bean() {
String[] strArray = { "a", "b", "c" };
Integer[] intArray = { 1, 2, 3, 4, 5 };
List<Integer> intList = new ArrayList<>();
intList.addAll(Arrays.asList(intArray));
List<Integer> intList = new ArrayList<>(Arrays.asList(intArray));
Map<String, Object> map = new HashMap<>();
map.put("name", "jack");
map.put("age", 18);
map.put("length", 175.3f);
TestBean bean = new TestBean();
Date date = Date.valueOf("2019-11-22");
LocalDateTime localDateTime = LocalDateTime.of(2000, 1, 1, 12, 0, 0);
Date date = DateExtUtils.localDateTime2Date(localDateTime);
LocalDate localDate = LocalDate.of(1949, 10, 1);
bean.setI1(10).setI2(1024).setF1(0.5f).setD1(100.0)
.setDate1(date).setDate2(localDateTime).setDate3(localDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public class TestBean implements Serializable {

private Map<String, Object> map;

public static enum Color {
public enum Color {
RED,
YELLOW,
BLUE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import static org.assertj.core.api.Assertions.assertThat;

/**
* 序列化、反序列化性能测试
*
* @author <a href="mailto:forbreak@163.com">Zhang Peng</a>
* @since 2019-11-22
*/
public class SerializeTest {
public class SerializePerformanceTest {

private static final int BATCH_SIZE = 100000;

Expand All @@ -21,9 +23,9 @@ public void testJdkSerialize() throws IOException, ClassNotFoundException {
long begin = System.currentTimeMillis();
for (int i = 0; i < BATCH_SIZE; i++) {
TestBean oldBean = BeanUtils.initJdk8Bean();
byte[] bytes = JdkSerializeDemo.serialize(oldBean);
byte[] bytes = JdkSerializeDemo.writeToBytes(oldBean);
assertThat(bytes).isNotEmpty();
TestBean newBean = JdkSerializeDemo.deserialize(bytes, TestBean.class);
TestBean newBean = JdkSerializeDemo.readFromBytes(bytes, TestBean.class);
assertThat(newBean).isNotNull();
}
long end = System.currentTimeMillis();
Expand All @@ -35,13 +37,27 @@ public void testFst() throws IOException {
long begin = System.currentTimeMillis();
for (int i = 0; i < BATCH_SIZE; i++) {
TestBean oldBean = BeanUtils.initJdk8Bean();
byte[] bytes = FstDemo.serialize(oldBean);
byte[] bytes = FstDemo.writeToBytes(oldBean);
assertThat(bytes).isNotEmpty();
TestBean newBean = FstDemo.deserialize(bytes, TestBean.class);
TestBean newBean = FstDemo.readFromBytes(bytes, TestBean.class);
assertThat(newBean).isNotNull();
}
long end = System.currentTimeMillis();
System.out.printf("FST 序列化/反序列化耗时:%s", (end - begin));
}

@Test
public void testKryo() throws IOException {
long begin = System.currentTimeMillis();
for (int i = 0; i < BATCH_SIZE; i++) {
TestBean oldBean = BeanUtils.initJdk8Bean();
byte[] bytes = KryoDemo.writeToBytes(oldBean);
assertThat(bytes).isNotEmpty();
TestBean newBean = KryoDemo.readFromBytes(bytes, TestBean.class);
assertThat(newBean).isNotNull();
}
long end = System.currentTimeMillis();
System.out.printf("Kryo 序列化/反序列化耗时:%s", (end - begin));
}

}