Protocol Buffer序列化Java框架-Protostuff
了解Protocol Buffer
首先要知道什么是Protocol Buffer,在编程过程中,当涉及数据交换时,我们往往需要将对象进行序列化然后再传输。常见的序列化的格式有JSON,XML等,这些格式虽然可读性较好,但占用的空间大小并不是最优的。基于此,Google创建了一种名叫Protocol Buffer的序列化格式,它与JSON,XML相比可读性较差,但占用的空间也会更小,在一些对于速度要求比较高的场景中较为常用。
Java序列化Protocol Buffer框架—ProtoStuff
Google对于Protocol Buffer提供了多种语言的实现方法:Java,C++,go和python。但我们在使用时仍然需要去编写可读性不高的.proto文件,然后使用Google提供的实现方法编译成对应的语言,这就提高了我们使用Protocol Buffer的门槛。因此ProtoStuff就诞生了,通过ProtoStuff这个框架,我们能直接将对象通过Protocol Buffer这种序列化的方式转成对应的字节,极大地降低了我们使用Protocol Buffer的使用成本。
实例
首先我们新建一个maven项目,然后添加ProtoStuff的依赖,其中Objenesis是一个用来实例化一个特定类的新对象的Java库。通过该库,我们能在不调用构造函数的情况下实例化一个类的对象。
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
<!-- Objenesis -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis.version}</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
然后我们创建两个POJO来进行序列化的测试
@Data
@Builder
public Class Goods {
private Integer num;
private String name;
private Double price;
}
@Data
@Builder
public Class Repository {
private String name;
private String location;
private List<Goods> goodsList;
}
再之后编写Protocol Buffer序列化的工具类
public Class SerializationUtil {
private static Map<Class<?>, Schema<?>> cacheSchema = new ConcurrentHashMap();
private static Objenesis objenesis = new ObjenesisStd(true);
/**
* 序列化(对象 -> 字节数组)
*
*/
public static <T> byte[] serialize(T obj) {
Class<T> cls = (Class<T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema<T> schema = getSchema(cls);
return ProtobufIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
/**
* 反序列化(字节数组 -> 对象)
*
*/
public static <T> T deserilize(byte[] data, Class<T> cls) {
try {
T message = objenesis.newInstance(cls);
Schema<T> schema = getSchema(cls);
ProtobufIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> cls) {
Schema<T> schema = (Schema<T>) cacheSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
cacheSchema.put(cls, schema);
}
return schema;
}
}
最后编写测试类来对序列化工具类进行测试
public Class Test {
public static void main(String[] args) {
Goods phone = Goods.builder().num(10).name("phone").price(1999.99).build();
Goods water = Goods.builder().num(100).name("water").price(1.00).build();
Repository repository = Repository.builder().name("Taobao").location("china").goodsList(Arrays.asList(phone, water)).build();
byte[] data = SerializationUtil.serialize(repository);
System.out.println("序列化结果:" + Arrays.toString(data));
Repository result = SerializationUtil.deserilize(data, Repository.class);
System.out.println("反序列化结果:" + result);
}
}
输出结果:
序列化结果:[10, 6, 84, 97, 111, 98, 97, 111, 18, 5, 99, 104, 105, 110, 97, 26, 18, 8, 10, 18, 5, 112, 104, 111, 110, 101, 25, 41, 92, -113, -62, -11, 63, -97, 64, 26, 18, 8, 100, 18, 5, 119, 97, 116, 101, 114, 25, 0, 0, 0, 0, 0, 0, -16, 63]
反序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])
与JSON的对比
首先导入JSON处理的依赖,这里我们使用jackson来对JSON进行处理
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
之后修改测试类
public class Test {
public static void main(String[] args) throws IOException {
Goods phone = Goods.builder().num(10).name("phone").price(1999.99).build();
Goods water = Goods.builder().num(100).name("water").price(1.00).build();
Repository repository = Repository.builder().name("Taobao").location("china").goodsList(Arrays.asList(phone, water)).build();
byte[] protobufData = SerializationUtil.serialize(repository);
System.out.println("ProtoBuf序列化结果:" + Arrays.toString(protobufData));
Repository protobufResult = SerializationUtil.deserilize(protobufData, Repository.class);
System.out.println("ProtoBuf反序列化结果:" + protobufResult);
ObjectMapper mapper = new ObjectMapper();
byte[] jsonData = mapper.writeValueAsBytes(repository);
System.out.println("JSON序列化结果:" + Arrays.toString(jsonData));
Repository jsonResult = mapper.readValue(jsonData, Repository.class);
System.out.println("JSON序列化结果:" + jsonResult);
System.out.println();
System.out.println("ProtoBuf序列化后字符串结果:" + new String(protobufData, StandardCharsets.UTF_8));
System.out.println("JSON序列化后字符串结果:" + new String(jsonData, StandardCharsets.UTF_8));
System.out.println();
System.out.println("ProtoBuf序列化长度:" + protobufData.length);
System.out.println("JSON序列化长度:" + jsonData.length);
}
}
输出结果:
ProtoBuf序列化结果:[10, 6, 84, 97, 111, 98, 97, 111, 18, 5, 99, 104, 105, 110, 97, 26, 18, 8, 10, 18, 5, 112, 104, 111, 110, 101, 25, 41, 92, -113, -62, -11, 63, -97, 64, 26, 18, 8, 100, 18, 5, 119, 97, 116, 101, 114, 25, 0, 0, 0, 0, 0, 0, -16, 63]
ProtoBuf反序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])
JSON序列化结果:[123, 34, 110, 97, 109, 101, 34, 58, 34, 84, 97, 111, 98, 97, 111, 34, 44, 34, 108, 111, 99, 97, 116, 105, 111, 110, 34, 58, 34, 99, 104, 105, 110, 97, 34, 44, 34, 103, 111, 111, 100, 115, 76, 105, 115, 116, 34, 58, 91, 123, 34, 110, 117, 109, 34, 58, 49, 48, 44, 34, 110, 97, 109, 101, 34, 58, 34, 112, 104, 111, 110, 101, 34, 44, 34, 112, 114, 105, 99, 101, 34, 58, 49, 57, 57, 57, 46, 57, 57, 125, 44, 123, 34, 110, 117, 109, 34, 58, 49, 48, 48, 44, 34, 110, 97, 109, 101, 34, 58, 34, 119, 97, 116, 101, 114, 34, 44, 34, 112, 114, 105, 99, 101, 34, 58, 49, 46, 48, 125, 93, 125]
JSON序列化结果:Repository(name=Taobao, location=china, goodsList=[Goods(num=10, name=phone, price=1999.99), Goods(num=100, name=water, price=1.0)])ProtoBuf序列化后字符串结果:
Taobaochina
phone)\���?�@dwater �?
JSON序列化后字符串结果:{"name":"Taobao","location":"china","goodsList":[{"num":10,"name":"phone","price":1999.99},{"num":100,"name":"water","price":1.0}]}ProtoBuf序列化长度:55
JSON序列化长度:131
从结果来看在可读性上显然JSON更加易读,ProtoBuf序列化后再转为字符串甚至会乱码,但在长度上则显然ProtoBuf更占优势,JSON的长度比ProtoBuf多了一倍多。
️:在使用Jackson进行JSON反序列化时我们需要对我们的POJO类添加有参和无参构造,即添加@NoArgsConstructor 和@AllArgsConstructor 这两个注解,否则会抛出如下异常:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of
com.xxx.xxx.Repository(no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"{"name":"Taobao","location":"china","goodsList":[{"num":10,"name":"phone","price":1999.99},{"num":100,"name":"water","price":1.0}]}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1209)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1400)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:362)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:195)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4593)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3609)
at com.silence.rpc.test.Test.main(Test.java:31)
原因是因为@Builder并不会添加无参构造,而Jackson的反序列化需要无参构造,因为在反序列化的时候,会先初始化对象,此时默认调用的是无参函数,然后再进行赋值,故此我们需要添加@NoArgsConstructor ,如果只添加这个注解,又会导致缺少有参构造,因此我们还需要添加@AllArgsConstructor 。
Protocol Buffer序列化Java框架-Protostuff的更多相关文章
- Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?
前言 习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存 ...
- Protocol buffer序列化及其在微信蓝牙协议中的应用
Protocol buffer是Google出品的一种轻便高效的结构化数据存储格式,可对结构化数据进行序列化,并具有语言无关.平台无关等特点,在通信协议和数据存储等领域已经得到广泛的应用.目前其已经提 ...
- Protocol Buffer序列化对比Java序列化.
初识 Protocol Buff是谷歌推出的一种序列化协议. 而Java序列化协议也是一种协议. 两者的目的是, 将对象序列化成字节数组, 或者说是二进制数据, 那么他们之间有什么差异呢. proto ...
- Protocol Buffer序列化/反序列化---初体验(java版)
今天闲遐时学习了 Protocol Buffer 在网上看到了许多资料,其中不泛精品,想要详细了解的请看文章结尾的友情链接,我这里就做加深印象,快速入门的一个完整的demo,仅此而已. 学完你可以得到 ...
- Protocol Buffer技术详解(Java实例)
Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...
- Android:Google出品的序列化神器Protocol Buffer使用攻略
习惯用 Json.XML 数据存储格式的你们,相信大多都没听过Protocol Buffer Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式 ...
- [java]序列化框架性能对比(kryo、hessian、java、protostuff)
序列化框架性能对比(kryo.hessian.java.protostuff) 简介: 优点 缺点 Kryo 速度快,序列化后体积小 跨语言支持较复杂 Hessian 默认支持跨语言 较慢 Pro ...
- Protocol Buffer使用转换工具将proto文件转换成Java文件流程及使用
Client与Server的网络通信协议传输使用google protobuf,服务器端使用的是Java 一. Protocol Buffersprotobuf全称Google Protocol Bu ...
- protocol buffer 整数序列化
http://blog.csdn.net/csfreebird/article/details/7624807 varints用于正整数 (无符号整数) varints 是 一个很不错的技术.将一个整 ...
随机推荐
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- 重磅!微软发布 vscode.dev,把 VS Code 带入浏览器!
早在 2019 年,当.dev顶级域名开放时,我们赶紧注册了vscode.dev.像许多购买.dev域名的人一样,我们不知道我们将用它做啥.反正,也占个坑吧! 将 VS Code 带入浏览器 直到今天 ...
- vue3.x移动端页面基于vue-router的路由切换动画
移动端页面切换一般都具有动画,我们既然要做混合开发,做完之后还是不能看起来就像一个网页,所以我们基于vue-router扩展了一个页面切换push和pop的动画.这是一篇比较硬核的帖子,作者花了不少精 ...
- Alpha发布声明
项目 内容 这个作业属于哪个课程 2021春季软件工程(罗杰 任健) 这个作业的要求在哪里 Alpha-发布声明 我们是谁 删库跑路对不队 我们在做什么 题士 进度如何 进度总览 一.功能与特性 1. ...
- vs2010中release模式下调试程序
debug模式调试信息全,但是速度很慢,在数据量比较大的时候非常影响调试效率,release模式速度快,但是没有调试信息.所以在编译的时候很多编译器会提供一种折中的编译方式,在release下提供调试 ...
- 网络原理数据链路层之差错控制(检错编码和纠错编码)->(奇偶校验码、CRC循环冗余码、海明码)
文章转自:https://blog.csdn.net/weixin_43914604/article/details/104864783 学习课程:<2019王道考研计算机网络> 学习目的 ...
- 攻防世界 杂项 4.something_in_image
这是原题 我这里使用编辑器打开,一看乱码也挺多的,于是想了想ctrl+f搜索一下flag关键字吧,结果答案出来了(flag不少,多搜索几次) Flag{yc4pl0fvjs2k1t7T}
- 【做题记录】CF1444A Division
CF1444A Division 题意: 给定 \(t\) 组询问,每组给两个数 \(p_i\) 和 \(q_i\) ,找出最大的整数 \(x_i\) ,要求 \(p_i\) 可被 \(x_i\) 整 ...
- hdu 1078 FatMouse and Cheese(记忆搜)
N*N的矩阵,每个格子上有一个值. 老鼠起始在(1,1),每次只能水平着走或垂直着走.且最多只能走K步.且走到的格子里的值必须比上一次呆的格子里的值大. 问老鼠最多收集到多少值. 思路: 记忆搜好写. ...
- XOR算法
原理 依据的是异或门 即同为0,异为1 0^0=0 0^1=1 1^0=1 1^1=0 对一个数据进行两次XOR运算会得到这个数据本身 所以加密时就将message和其对应的key进行一波XOR运算得 ...