(记录)Jedis存放对象和读取对象--Java序列化与反序列化

  一、理论分析

  在学习Redis中的Jedis这一部分的时候,要使用到Protostuff(Protobuf的Java客户端)这一序列化工具。一开始看到序列化这些字眼的时候,感觉到一头雾水。于是,参考了网上的很多资料,理解了Java序列化与反序列化(参考https://blog.csdn.net/wangloveall/article/details/7992448/),Protobuf的作用以及在Java中的使用方法。

  1.Java序列化与反序列化是什么:

  Java序列化是指把Java对象转换为字节序列的过程;而Java反序列化是指把字节序列恢复为Java对象的过程。

  2.为什么需要Java序列化与反序列化

  序列化的两种非常重要的应用:

  • 使用序列化将对象集合保存到磁盘文件中,并按照它们被存储的样子获取它们。
  • 通过网络将对象集合传送到另一台计算机上。

  3.为什么只能用序列化和反序列化

  • 在磁盘文件中,不能去保存和恢复对象的内存地址是因为对象被重载时,它可能占据的是与原来完全不同的内存地址。
  • 在网络传输中,不同的处理器之间通信时,对象占据的内存地址也是完全不同。

  4.序列化和反序列化的好处

  序列化就是每个对象都是用一个序列号保存的,这就是这种机制被称为序列化的原因。

  5.实现Java对象序列化与反序列化的方法

  • JDK库中的序列化API实现二进制序列化
  • XML
  • JSON
  • Protostaff

  6.JDK库中序列化与反序列化:

  (1)对象序列化包括如下步骤:

  • 创建一个对象输出流,它可以包装一个其他类的目标输出流,如文件输出流;
  • 通过对象输出流的writeObject()方法写对象。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("empoyee.dat"));
Employee harry = new Employee("Harry Hacker", 5000, 1989, 10, 1);
Maneger boss = new Manager("Carl Cracker", 7000, 1984, 12, 15);
oos.writeObject(harry);
oos.writeObject(boss);

  (2)对象反序列化包括如下步骤:

  • 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  • 通过对象输入流的readObject()方法以这些对象被写出的顺序读取对象并获得它们。
ObjectInputStream ois = new ObjectInputStream (new FileInputStream("empoyee.dat"));
Employee e1 = (Employee)ois.readObject();
Maneger e2 = (Manager)ois.readObject();

  (3)对于那么需要序列化与反序列化的对象,对应的类必须要实现JDK库的相关API,有以下三种方法:

  • 若Club类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化

    ObjectOutputStream采用默认的序列化方式,对Club对象的非transient的实例变量进行序列化。

    ObjcetInputStream采用默认的反序列化方式,对对Club对象的非transient的实例变量进行反序列化。

  • 若Club类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。

    ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。

    ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。

  • 若Club类实现了Externalnalizable接口,且Club类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。

    ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。

    ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。

  二、示例程序分析

  1.测试环境:

  • 虚拟机ubuntu 16.04
  • Jedis版本:Jedis-2.9.0.jar
  • redis.conf配置文件写入ubuntu系统ifconfig读取的ip地址:192.168.131.130
  • 主机ping 192.168.131.130可以ping通。  

  2.主机上运行eclipse程序,证明可以连接到redis服务器

package bigjun.iplab.jedisConnectTest;

import redis.clients.jedis.Jedis;

public class JedisConnectTest {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        Jedis jedis = new Jedis("192.168.131.130");
        jedis.set("JedisConnectTest", "pass");
        String getResult = jedis.get("JedisConnectTest");
        System.out.println(getResult);
        
    }
}

  控制台输出pass,同时查看ubuntu系统上redis中成功存入redis数据。

  3.Jedis将对象序列化为二进制的API:

public String set(final String key, String value)
public String set(final byte[] key, final byte[] value)
public byte[] get(final byte[] key)
public String get(final String key)

  有了这些API的支持,就可以将Java对象序列化为二进制,当应用需要获取Java对象时,使用get(final byte[]key)函数将字节数组取出,然后反序列化为Java对象即可。

  和很多NoSQL数据库(例如Memcache、Ehcache)的客户端不同,Jedis本身没有提供序列化的工具,也就是说开发者需要自己引入序列化的工具。

  4.在测试主机可以成功连接到虚拟机上的redis服务器并且可以存读数据后,考虑如何存读对象。

  建立一个实现了Serializable接口的简单对象类Club:

package bigjun.iplab.jdk;

import java.io.Serializable;
import java.util.Date;

public class Club implements Serializable {
    
    /**
     * 其实序列化的作用是能转化成Byte流,然后又能反序列化成原始的类。能
     * 在网络进行传输,也可以保存在磁盘中,
     * 有了SUID之后,那么如果序列化的类已经保存了在本地中,
     * 中途你更改了类后,SUID变了,那么反序列化的时候就不会变成原始的类了,
     * 还会抛异常,主要就是用于版本控制。
     */
    private static final long serialVersionUID = 1L;
    
    private int id;
    private String name;
    private String info;
    private Date createDate;
    private int rank;
    
    public Club() {
        
    }
    
    public Club(int id, String name, String info, Date createDate, int rank) {
        super();
        this.id = id;
        this.name = name;
        this.info = info;
        this.createDate = createDate;
        this.rank = rank;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getInfo() {
        return info;
    }
    public void setInfo(String info) {
        this.info = info;
    }
    public Date getCreateDate() {
        return createDate;
    }
    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }
    public int getRank() {
        return rank;
    }
    public void setRank(int rank) {
        this.rank = rank;
    }
 

  5.使用JDK库实现序列化和反序列化:

package bigjun.iplab.jdk;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

import redis.clients.jedis.Jedis;

public class JDKSerializetionUtil {

    public static void main(String[] args) {
        Jedis jedis = null;
        try {
            jedis = new Jedis("192.168.131.130");

            Club club = new Club();

            club.setId(1);
            club.setName("AC");
            club.setInfo("米兰");
            club.setCreateDate(new Date());
            club.setRank(2);

            jedis.set("JDK".getBytes(), serialize(club));
            byte[] getByte = jedis.get("JDK".getBytes());
            Object getObject = unserizlize(getByte);
            if (getObject instanceof Club) {
                System.out.println(getObject);
                System.out.println(((Club) getObject).getId());
                System.out.println(((Club) getObject).getName());
                System.out.println(((Club) getObject).getInfo());
                System.out.println(((Club) getObject).getCreateDate());
                System.out.println(((Club) getObject).getRank());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    private static byte[] serialize(Object object) {
        ObjectOutputStream objectOutputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(object);
            byte[] getByte = byteArrayOutputStream.toByteArray();
            return getByte;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Object unserizlize(byte[] binaryByte) {
        ObjectInputStream objectInputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        byteArrayInputStream = new ByteArrayInputStream(binaryByte);
        try {
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Object obj = objectInputStream.readObject();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

  输出为:

bigjun.iplab.jdk.Club@300ffa5d
1
AC
米兰
Sun Jun 03 20:29:24 GMT+08:00 2018
2

  在虚拟机上运行get JDK得到:

192.168.131.130:6379> get JDK
"xacxedx00x05srx00x15bigjun.iplab.jdk.Clubx00x00x00x00x00x00x00x01x02x00x05Ix00x02idIx00x04rankLx00
createDatetx00x10Ljava/util/Date;Lx00x04infotx00x12Ljava/lang/String;Lx00x04nameqx00~x00x02xpx00x00x00x01x00x00x00x02srx00x0ejava.util.Datehjx81x01KYtx19x03x00x00xpwx00x00x01cxc5x95xa00xtx00x06xe7xb1xb3xe5x85xb0tx00x02AC"

  再来看一下序列化方法:

    private static byte[] serialize(Object object) {
        ObjectOutputStream objectOutputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(object);
            byte[] getByte = byteArrayOutputStream.toByteArray();
            return getByte;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

  ByteArrayOutputStream:

  ObjectOutputStream

  再来看一下反序列方法:

    private static Object unserizlize(byte[] binaryByte) {
        ObjectInputStream objectInputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        byteArrayInputStream = new ByteArrayInputStream(binaryByte);
        try {
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            Object obj = objectInputStream.readObject();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

  ByteArrayInputStream

  ObjectInputStream

  6.使用Xml实现序列化和反序列化

  首先,必须要在要被序列化的对象的类上注释@XmlRootElement(name = "Club"),即:

package bigjun.iplab.xml;

import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;

// Xml文件中的根标识,必须要表明这个元素,可以让对象和Xml之间方便转换
@XmlRootElement(name = "Club") 
public class Club implements Serializable {  
  ...
}

  然后是在Jedis中利用Xml读写Club对象:

package bigjun.iplab.xml;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.Date;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import redis.clients.jedis.Jedis;

public class XMLSerializetionUtil {

    public static void main(String[] args) {
        Jedis jedis = null;
        try {
            jedis = new Jedis("192.168.131.130");

            Club club = new Club();

            club.setId(2);
            club.setName("RM");
            club.setInfo("皇马");
            club.setCreateDate(new Date());
            club.setRank(1);

            jedis.set("XML", serialize(club));
            String getString = jedis.get("XML");
            Object getObject = unserizlize(Club.class, getString);
            if (getObject instanceof Club) {
                System.out.println(getObject);
                System.out.println(((Club) getObject).getId());
                System.out.println(((Club) getObject).getName());
                System.out.println(((Club) getObject).getInfo());
                System.out.println(((Club) getObject).getCreateDate());
                System.out.println(((Club) getObject).getRank());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    private static String serialize(Object object) {
        StringWriter stringWriter = null;
        try {
            stringWriter = new StringWriter();
            JAXBContext jContext = JAXBContext.newInstance(object.getClass());
            Marshaller marshaller = jContext.createMarshaller();
            marshaller.marshal(object, stringWriter);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return stringWriter.toString();
    }

    private static Object unserizlize(Class<Club> clazz, String xmlString) {

        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader stringReader = new StringReader(xmlString);
            xmlObject = unmarshaller.unmarshal(stringReader);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }

}

  输出为:

bigjun.iplab.xml.Club@1f17ae12
2
RM
皇马
Mon Jun 04 09:42:17 GMT+08:00 2018
1

  在虚拟机上运行get XML得到:

"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Club><createDate>2018-06-04T09:42:17.343+08:00</createDate><id>2</id><info>xe7x9ax87xe9xa9xac</info><name>RM</name><rank>1</rank></Club>"

  再来分析一下序列化方法:

    private static String serialize(Object object) {
        StringWriter stringWriter = null;
        try {
            stringWriter = new StringWriter();
            JAXBContext jContext = JAXBContext.newInstance(object.getClass());
            Marshaller marshaller = jContext.createMarshaller();
            marshaller.marshal(object, stringWriter);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return stringWriter.toString();
    }

  再来分析一下反序列化方法:

    private static Object unserizlize(Class<Club> clazz, String xmlString) {

        Object xmlObject = null;
        try {
            JAXBContext context = JAXBContext.newInstance(clazz);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            StringReader stringReader = new StringReader(xmlString);
            xmlObject = unmarshaller.unmarshal(stringReader);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return xmlObject;
    }

  7.使用JSON实现序列化和反序列化

  Java下常用的JSON工具类库主要有以下几种:

  • JSON-lib
  • FastJson
  • Jackson
  • Gson

  1.以JSON-lib为例:

  所需的jar包为:

  

  Club类不需要添加任何注释,直接看在Jedis中通过JSON读写Club对象类:

package bigjun.iplab.json;

import java.util.Date;

import net.sf.ezmorph.object.DateMorpher;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;
import redis.clients.jedis.Jedis;

public class JDKSerializetionUtil {

    public static void main(String[] args) {
        Jedis jedis = null;
        try {
            jedis = new Jedis("192.168.131.130");

            Club club = new Club();

            club.setId(3);
            club.setName("CLE");
            club.setInfo("骑士");
            club.setCreateDate(new Date());
            club.setRank(2);

            jedis.set("JSON", serialize(club));
            String getByte = jedis.get("JSON");
            Object getObject = unserizlize(getByte);
            if (getObject instanceof Club) {
                System.out.println(getObject);
                System.out.println(((Club) getObject).getId());
                System.out.println(((Club) getObject).getName());
                System.out.println(((Club) getObject).getInfo());
                System.out.println(((Club) getObject).getCreateDate());
                System.out.println(((Club) getObject).getRank());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    private static String serialize(Object object) {
        JSONObject jsonObject = JSONObject.fromObject(object);
        String getString = jsonObject.toString();
        return getString;

    }

    private static Object unserizlize(String jsonString) {
        new JSONObject();
        JSONObject jObject = JSONObject.fromObject(jsonString);
        JSONUtils.getMorpherRegistry().registerMorpher(new DateMorpher(new String[] { "MM/dd/yyyy HH:mm:ss" }));
        Object jsonObject = JSONObject.toBean(jObject, Club.class);
        return jsonObject;
    }

}

  输出为:

六月 04, 2018 11:02:31 上午 net.sf.json.JSONObject toBean
信息: Property 'day' of class java.util.Date has no write method. SKIPPED.
六月 04, 2018 11:02:31 上午 net.sf.json.JSONObject toBean
信息: Property 'timezoneOffset' of class java.util.Date has no write method. SKIPPED.
bigjun.iplab.json.Club@2a098129
3
CLE
骑士
Mon Jun 04 11:02:30 GMT+08:00 2018
2

  在Redis服务器所在的虚拟机上执行get JSON输出为:

192.168.131.130:6379> get JSON
"{"createDate":{"date":4,"day":1,"hours":11,"minutes":2,"month":5,"seconds":30,"time":1528081350946,"timezoneOffset":-480,"year":118},"id":3,"info":"xe9xaax91xe5xa3xab","name":"CLE","rank":2}"

  分析使用JSON实现序列化代码:将Java对象转换为JSON对象--将JSON对象转换为String类型的字符串。

    private static String serialize(Object object) {
        JSONObject jsonObject = JSONObject.fromObject(object);
        String getString = jsonObject.toString();
        return getString;
    }

  分析使用JSON实现反序列化代码:将String类型的JSON字符串转换为JSON对象----将JSON对象转换为Java对象。

    private static Object unserizlize(String jsonString) {
        new JSONObject();
        JSONObject jObject = JSONObject.fromObject(jsonString);
     // 防止转换时间错误 JSONUtils.getMorpherRegistry().registerMorpher(
new DateMorpher(new String[] { "MM/dd/yyyy HH:mm:ss" })); Object jsonObject = JSONObject.toBean(jObject, Club.class); return jsonObject; }

  2.以FastJson为例:

  (1)FastJson介绍

  Fastjson是一个Java语言编写的高性能功能完善的JSON库。它采用一种假定有序快速匹配的算法,把JSON Parse的性能提升到极致,是目前Java语言中最快的JSON库。Fastjson接口简单易用,已经被广泛使用在缓存序列化、协议交互、Web输出、Android客户端等多种应用场景。

  (2)FastJson优点:

  • FastJson数度快,无论序列化和反序列化,都是目前Java中最快的。
  • 功能强大(支持普通JDK类包括任意Java Bean Class、Collection、Map、Date或enum)。
  • 零依赖(没有依赖其它任何类库)

  (3)FastJson主要API

1. 将对象序列化成json字符串

String com.alibaba.fastjson.JSON.toJSONString(Object object)

2. 将json字符串反序列化成对象

<T> Project com.alibaba.fastjson.JSON.parseObject(String text, Class<T> clazz)

3. 将json字符串反序列化成JSON对象

JSONObject com.alibaba.fastjson.JSON.parseObject(String text)

4.根据key 得到json中的json数组

JSONArray com.alibaba.fastjson.JSONObject.getJSONArray(String key)

5. 根据下标拿到json数组的json对象

JSONObject com.alibaba.fastjson.JSONArray.getJSONObject(int index)

6.. 根据key拿到json的字符串值

String com.alibaba.fastjson.JSONObject.getString(String key)

7. 根据key拿到json的int值

int com.alibaba.fastjson.JSONObject.getIntValue(String key)

8. 根据key拿到json的boolean值

boolean com.alibaba.fastjson.JSONObject.getBooleanValue(String key)

  (4)使用FastJson实现在Jedis中读写Club对象(FastJson提供了很完美的API,这里就不用再多说了,直接使用即可)

  FastJson版本:fastjson-1.2.47.jar

package bigjun.iplab.json;

import java.util.Date;

import com.alibaba.fastjson.JSON;

import redis.clients.jedis.Jedis;

public class FastJsonSerializetionUtil {

    public static void main(String[] args) {
        Jedis jedis = null;
        try {
            jedis = new Jedis("192.168.131.130");

            Club club = new Club();

            club.setId(4);
            club.setName("GS");
            club.setInfo("勇士");
            club.setCreateDate(new Date());
            club.setRank(1);

            jedis.set("FastJson", serialize(club));
            String getByte = jedis.get("FastJson");
            Object getObject = unserizlize(getByte);
            if (getObject instanceof Club) {
                System.out.println(getObject);
                System.out.println(((Club) getObject).getId());
                System.out.println(((Club) getObject).getName());
                System.out.println(((Club) getObject).getInfo());
                System.out.println(((Club) getObject).getCreateDate());
                System.out.println(((Club) getObject).getRank());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    private static String serialize(Object object) {
        String getString = JSON.toJSONString(object);
        return getString;
    }

    private static Object unserizlize(String jsonString) {
        Object jsonObject = JSON.parseObject(jsonString, Club.class);
        return jsonObject;
    }

}

  输出:

bigjun.iplab.json.Club@41975e01
4
GS
勇士
Mon Jun 04 11:25:04 GMT+08:00 2018
1

  虚拟机上执行get FastJson的输出(和使用JSON-lib方式输出的字符串明显短了很多):

192.168.131.130:6379> get FastJson
"{"createDate":1528082704016,"id":4,"info":"xe5x8bx87xe5xa3xab","name":"GS","rank":1}"

  8.JSON与XML优缺点对比分析

  (1)JSON的优点:

  • 与XML相比,JSON更加的简洁,我们可以一眼就看出其中的内容,方便检查排错
  • JSON更加轻量级,不管是编写,传输,还是解析都更加高效
  • JSON在传输过程中采用了压缩技术,更加的节省宽带
  • 众多的语言支持,如javascript,python,C,C++等主流语言都支持
  • 因为JSON格式能直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,且完成任务不变,并且易于维护。

  (2)XML的优点:

  • 格式统一,符合标准;
  • 容易与其他系统进行远程交互,数据共享比较方便

  (3)XML的缺点:

  • XML文件庞大,文件格式复杂,传输占带宽;
  • 服务器端和客户端都需要花费大量代码来解析XML,导致服务器端和客户端代码变得异常复杂且不易维护;
  • 客户端不同浏览器之间解析XML的方式不一致,需要重复编写很多代码;
  • 服务器端和客户端解析XML花费较多的资源和时间。

  9.使用Protostuff实现序列化与反序列化

  (1)Protobuf介绍

  Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

  Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

  (2)Protobuf优点

  • 平台无关,语言无关,可扩展;  
  • 提供了友好的动态库,使用简单;
  • 解析速度快,比对应的XML快约20-100倍;  
  • 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10;
  • 独立于语言,独立于平台,最最重要的是它的效率相当高,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二进制序列化的10分之一,

  (3)Protobuf主要流程

  需要自己写一个.proto文件用来描述序列化的格式,然后用Protobuf提供的protoc工具将.proto文件编译成一个Java文件,最后将该Java文件引入到项目中就可以了。

  (4)Protostuff介绍

  google原生的protobuffer使用起来相当麻烦,首先要写.proto文件,然后编译.proto文件,生成对应的.java文件。protostuff基于Google Protobuf,好处就是不用自己写.proto文件同时在几乎不损耗性能的情况下即可实现对象的序列化与反序列化。

  (5)使用Protostuff示例

  Protostuff版本:

  

  使用Protostuff实现Jedis中Club对象的读取:

  • 代码结构为:

  

  序列化工具类ProtostuffSerializer 提供了序列化和反序列化方法:

package bigjun.iplab.protostuff;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;

/**
 * 序列化和反序列化工具
 */
public class ProtostuffSerializer {
    private Schema<Club> schema = RuntimeSchema.createFrom(Club.class);
    
    // 序列化工具
    public byte[] seriable(final Club club) {
        final LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            return serializeInternal(club, schema, linkedBuffer);
        } catch (final Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            linkedBuffer.clear();
        }
    }
    
    // 实际序列化工具
    private <T> byte[] serializeInternal(final T source, final Schema<T> schema, final LinkedBuffer linkedBuffer) {
        return ProtostuffIOUtil.toByteArray(source, schema, linkedBuffer);
    }
    
    // 反序列化工具
    public Club deserialize(final byte[] bytes) {
        try {
            Club club = deserializeInternal(bytes, schema.newMessage(), schema);
            if (club != null) {
                return club;
            }
        } catch (final Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        return null;
    }
    
    // 实际反序列化工具
    private <T> T deserializeInternal(final byte[] bytes, final T result, final Schema<T> schema) {
        ProtostuffIOUtil.mergeFrom(bytes, result, schema);
        return result;
    }
}

  测试类:

package bigjun.iplab.protostuff;

import java.util.Date;

import redis.clients.jedis.Jedis;

public class ProtostuffSerializetionUtil {

    public static void main(String[] args) {
        Jedis jedis = null;
        try {
            // 生成Jedis对象
            jedis = new Jedis("192.168.131.130");
            // 生成序列化和反序列化工具类对象
            ProtostuffSerializer protostuffSerializer = new ProtostuffSerializer();
            // 定义实体对象
            Club club = new Club(5, "RNG", "皇族", new Date(), 1);
            
            // 序列化
            byte[] clubBytes = protostuffSerializer.serialize(club);
            // 将club对象写入Redis
            jedis.set("Protostuff".getBytes(), clubBytes);
            
            // 从Redis中读取表示club对象的字符数组
            byte[] getBytes = jedis.get("Protostuff".getBytes());
            // 反序列化
            Club getClubObject = protostuffSerializer.deserialize(getBytes); 
            
            if (getClubObject instanceof Club) {
                System.out.println(getClubObject);
                System.out.println(((Club) getClubObject).getId());
                System.out.println(((Club) getClubObject).getName());
                System.out.println(((Club) getClubObject).getInfo());
                System.out.println(((Club) getClubObject).getCreateDate());
                System.out.println(((Club) getClubObject).getRank());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }
}

  输出:

bigjun.iplab.protostuff.Club@ed17bee
5
RNG
皇族
Mon Jun 04 15:55:32 GMT+08:00 2018
1

  虚拟机上执行get Protostuff的输出(可以看出,长度在几种序列化中最短):

192.168.131.130:6379> get Protostuff
"x05x12x03RNGx1ax06xe7x9ax87xe6x97x8f!xf3Gxcbxc9cx01x00x00(x01"

  再来看一下序列化工具:

    // 序列化工具
    public byte[] serialize(final Club club) {
        final LinkedBuffer linkedBuffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            return serializeInternal(club, schema, linkedBuffer);
        } catch (final Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            linkedBuffer.clear();
        }
    }

    // 实际序列化工具
    private <T> byte[] serializeInternal(final T source, final Schema<T> schema, final LinkedBuffer linkedBuffer) {
        return ProtostuffIOUtil.toByteArray(source, schema, linkedBuffer);
    }

  再看看一下反序列化工具:

// 反序列化工具
    public Club deserialize(final byte[] bytes) {
        try {
            Club club = deserializeInternal(bytes, schema.newMessage(), schema);
            if (club != null) {
                return club;
            }
        } catch (final Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        return null;
    }

    // 实际反序列化工具
    private <T> T deserializeInternal(final byte[] bytes, final T result, final Schema<T> schema) {
        ProtostuffIOUtil.mergeFrom(bytes, result, schema);
        return result;
    }

  代码已上传至GitHub:https://github.com/BigJunOba/JedisSerialization

  

原文地址:https://www.cnblogs.com/BigJunOba/p/9127414.html