序列化与反序列化之 Protostuff

本文详细介绍了Java中序列化与反序列化的重要性和应用场景,包括Google的Protobuf、Java原生序列化以及ProtoStuff框架的使用。通过实例展示了如何使用ProtoStuff进行序列化和反序列化操作,适用于数据传输和持久化存储。
摘要由CSDN通过智能技术生成

一 序列化与反序列化

在大型开发中,序列化与反序列化是一个常见的技术点和问题。在之前我们对序列化与反序列化有过相关描述,但并不系统,更偏重于原理介绍。这里,我们讲详细介绍序列化与反序列化的更多场景和应用实践。

二 概念回顾

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个 Java 对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为 Java 对象。

把 Java 对象转换为字节序列的过程称为对象的序列化

把字节序列恢复为 Java 对象的过程称为对象的反序列化

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

三 技术方案

3.1 google 的 protobuffer

早期 google 原生的 protobuffer 使用起来相当麻烦,首先要写.proto 文件,然后编译.proto 文件,生成对应的.java 文件,过程较为繁琐。不过目前随着 protobuf-java 等 jar 包的提供,使用也简化了很多。

3.2 Java 的序列化与反序列化

根据 Thinking in java 3rd Edition 的描述:

利用对象序列化可以实现“轻量级持久化”(lightweight persistence)。“持久化”意味着一个对象的生存周期并不取决于程序是否正在执行;它可以生存于程序的调用之间。通过将一个序列化对象写入磁盘,然后在重新调用时恢复该对象,就能够实现持久化的效果。之所以称其为“轻量级”,是因为不能用某种“persistent”(持久)关键字来简单地定义一个对象,并让系统自动维护其他细节问题(尽管将来有可能实现)。相反,对象必须在程序中显式地序列化和重组。如果需要一个更严格的持久化机制,可以考虑使用 Java 数据对象(JDO)或者像 Hibernate 之类的工具。

对象序列化的概念加入到语言中是为了提供对两种主要特性的支持:

·Java 的“远程方法调用”(RMI,Remote Method Invocation)使存活于其他计算机上的对象使用起来就像是存活于本机上一样。当向远程对象发送消息时,需要通过对象序列化来传输参数和返回值。

·对 Java Beans 来说对象序列化也是必需的。使用一个 Bean 时,一般情况下是在设计阶段对它的状态信息进行配置。这种状态信息必须保存下来,并在程序启动以后,进行恢复;具体工作由对象序列化完成。

3.3 io.protostuff

这是最近在项目中使用到的一个框架,本篇也将先详细描述相关的使用案例,作为本系列的开篇。

四 使用示例

4.1 引入 maven 依赖

使用的是 1.5.3 版本:

<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.5.3</version>
</dependency>
<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.5.3</version>
</dependency>

4.2 定义实体类

4.2.1 ProtoBufEntity

package com.flamingskys.toolbox.tool.protobuf.entity;

import lombok.Data;

@Data
public class ProtoBufEntity {
    private String name;
    private int age;
    private String label;

    public ProtoBufEntity(){

    }

    public ProtoBufEntity(String name, Integer age){
        this.name = name;
        this.age = age;
    }
}

4.2.2 GroupEntity

这是一个嵌套实体类,包含了上面的 ProtoBufEntity:

package com.flamingskys.toolbox.tool.protobuf.entity;

import lombok.Data;

import java.util.List;

@Data
public class GroupEntity {
    private String id;

    private String name;

    private List<ProtoBufEntity> entities;

    public GroupEntity(String id, String name, List<ProtoBufEntity> entities) {
        this.id = id;
        this.name = name;
        this.entities = entities;
    }
}

4.2.3 应用类 ProtoStuffUtils

package com.flamingskys.toolbox.tool.protobuf;

import com.flamingskys.toolbox.tool.protobuf.entity.GroupEntity;
import com.flamingskys.toolbox.tool.protobuf.entity.ProtoBufEntity;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ProtoStuffUtils {
    /**
     * 避免每次序列化都重新申请Buffer空间
     */
    private static LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    /**
     * 缓存Schema
     */
    private static Map<Class<?>, Schema<?>> schemaCache = new ConcurrentHashMap<Class<?>, Schema<?>>();

    /**
     * 序列化方法,把指定对象序列化成字节数组
     *
     * @param obj
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clazz);
        byte[] data;
        try {
            data = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } finally {
            buffer.clear();
        }

        return data;
    }

    /**
     * 反序列化方法,将字节数组反序列化成指定Class类型
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        Schema<T> schema = getSchema(clazz);
        T obj = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, obj, schema);
        return obj;
    }

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) schemaCache.get(clazz);
        if (schema == null) {
            //这个schema通过RuntimeSchema进行懒创建并缓存
            //所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
            schema = RuntimeSchema.getSchema(clazz);
            if (schema == null) {
                schemaCache.put(clazz, schema);
            }
        }

        return schema;
    }

    public static void main(String[] args){
        final ProtoBufEntity entity1 = new ProtoBufEntity("aaa",20);
        final ProtoBufEntity entity2 = new ProtoBufEntity("bbb",21);
        List<ProtoBufEntity> entitys = new ArrayList<ProtoBufEntity>(){
            {
                add(entity1);
                add(entity2);
            }
        };
        GroupEntity group = new GroupEntity("id_1","group1", entitys);

        byte[] bytes = ProtoStuffUtils.serialize(group);
        System.out.println("序列化后: " + bytes.length);

        GroupEntity group1 = ProtoStuffUtils.deserialize(bytes,GroupEntity.class);
        System.out.println("反序列化后: " + group1.getName());

    }
}

本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
首先,需要在前端项目中引入 `protostuff` 库。可以通过 npm 安装或者使用 `<script>` 标签引入。 接下来,需要定义用于序列化反序列化的数据结构。可以使用 protobuf 定义数据结构,然后通过 `protostuff` 的 `Message` 类来创建 JavaScript 类。 例如,假设我们有一个 protobuf 文件定义了一个 `Person` 消息类型: ```protobuf syntax = "proto3"; message Person { string name = 1; int32 age = 2; } ``` 我们可以使用 `protoc` 编译器来生成对应的 JavaScript 代码: ``` protoc --js_out=import_style=commonjs,binary:. person.proto ``` 然后在前端项目中引入生成的代码: ```javascript const ProtoBuf = require('protobufjs'); const proto = require('./person_pb'); // create a Person message const person = new proto.Person(); person.setName('Alice'); person.setAge(30); // serialize the message to a Uint8Array const buffer = proto.Person.encode(person).finish(); // deserialize the message from a Uint8Array const decodedPerson = proto.Person.decode(buffer); console.log(decodedPerson.getName()); // output: "Alice" console.log(decodedPerson.getAge()); // output: 30 ``` 在这个例子中,我们使用 `Person` 类来创建一个 `person` 对象,并设置其属性。然后将其序列化为一个 `Uint8Array`,并在反序列化时使用 `Person` 类来解码该数组并还原为 `decodedPerson` 对象。 需要注意的是,`protostuff` 库本身并不支持在浏览器中使用,因为它依赖于 Node.js 的 `Buffer` 类。但是可以使用一些类似 `buffer` 的库,如 `typedarray-to-buffer` 或 `base64-arraybuffer`,将 `Uint8Array` 转换为 `buffer`,然后再进行序列化反序列化操作。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值