自定义redis序列化工具

redis一个优点就是可以将数据写入到磁盘中。

我们知道写入磁盘的数据实际上都是以字节(0101这样的二进制数据)的形式写入的。

这意味着如果我们要将一个对象写入磁盘,就必须将这个对象序列化。

java的序列化机制可以参考这篇文章

可以看到java的反序列是否成功跟serialVersionUID有很大的关系,自动生成的UID在每次编译时就会发生变化。

如果有两个程序共享一个redis,这个时候反序列化就会出现问题。

所以总监叫我自定义个redis序列化工具。

一、为什么Spring redis中缓存的对象需要实现 Serializable 序列化接口

查看RedisTemplate源码,我们可以看到,在RedisTemplate中针对不同类型的数据提供了不同的序列化方式。

默认的序列化方式为JdkSerializationRedisSerializer。

而我们常用的配置为键采用StringRedisSerializer来序列化,value采用默认的JdkSerializationSerializer。

这里我们首先分析一下这个两个类源码。

1、StringRedisSerializer

public class StringRedisSerializer implements RedisSerializer<String> {

    private final Charset charset;

    public StringRedisSerializer() {
        this(Charset.forName("UTF8"));
    }

    public StringRedisSerializer(Charset charset) {
        Assert.notNull(charset);
        this.charset = charset;
    }

    public String deserialize(byte[] bytes) {
        return (bytes == null ? null : new String(bytes, charset));
    }

    public byte[] serialize(String string) {
        return (string == null ? null : string.getBytes(charset));
    }
}

代码很简单,序列化方法就是直接将String转化为byte,反序列化就是直接将byte转化为String。

这里是不涉及serialVersionUID的(没有要求类必须实现Serializable接口)。

那么为什么会有redis缓存的对象必须实现Serializable接口的说法呢?

原因就在默认的序列化方法 JdkSerializationSerializer 中。

2、JdkSerializationSerializer

public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {

    private Converter<Object, byte[]> serializer = new SerializingConverter();
    private Converter<byte[], Object> deserializer = new DeserializingConverter();

    public Object deserialize(byte[] bytes) {
        if (SerializationUtils.isEmpty(bytes)) {
            return null;
        }

        try {
            return deserializer.convert(bytes);
        } catch (Exception ex) {
            throw new SerializationException("Cannot deserialize", ex);
        }
    }

    public byte[] serialize(Object object) {
        if (object == null) {
            return SerializationUtils.EMPTY_ARRAY;
        }
        try {
            return serializer.convert(object);
        } catch (Exception ex) {
            throw new SerializationException("Cannot serialize", ex);
        }
    }
}

上面是JdkSerializationSerializer 的源码。可以看到序列化的时候调用了serializer.convert方法。

下面是serializer.convert方法的源码

public byte[] convert(Object source) {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(256);
        try  {
            this.serializer.serialize(source, byteStream);
            return byteStream.toByteArray();
        }
        catch (Throwable ex) {
            throw new SerializationFailedException("Failed to serialize object using " +
                    this.serializer.getClass().getSimpleName(), ex);
        }
    }

默认情况下,this.serializer.serialize(source, byteStream)调用的是 DefaultSerializer 下的serialize方法。

public class DefaultSerializer implements Serializer<Object> {

    /**
     * Writes the source object to an output stream using Java Serialization.
     * The source object must implement {@link Serializable}.
     */
    public void serialize(Object object, OutputStream outputStream) throws IOException {
        if (!(object instanceof Serializable)) {
            throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
                    "but received an object of type [" + object.getClass().getName() + "]");
        }
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(object);
        objectOutputStream.flush();
    }

}

从上面的代码可以看到DefaultSerializer 下的serialize方法对Object对象的序列化方式是使用ObjectOutputStream 将对象写入到outputStream中的。

下面是ObjectOutputStream 的API。可以看到只有支持 java.io.Serializable 序列化接口的对象才能使用ObjectOutputStream进行写入与读取。

这就是为什么我们使用redis缓存对象时候需要让对象实现java.io.Serializable 序列化接口的原因。

 二、定制我们的序列化工具

为了不实现序列化接口,并且缓存到redis中不是难看的字节数据,我定制了自己的序列化工具。

序列化类需要实现 RedisSerializer<Object> 接口,并注册到Spring中。

原理很简单,序列化的时候将对象转换为JSONObject,然后将JSONObject转换为String,最后转化为byte数组。

反序列化的时候,则是将byte数组转化为JSONObject,RedisTemplate从redis中get的对象就是JSONObject类型。

下面是我的代码。

package com.zkxl.fep.redis;

import java.nio.charset.Charset;

import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;

import net.sf.json.JSONObject;

public class SerializeUtil implements RedisSerializer<Object>{

    static final byte[] EMPTY_ARRAY = new byte[0];
    private final Charset charset;

    public SerializeUtil() {
        // TODO Auto-generated constructor stub
        this(Charset.forName("UTF8"));
    }

    public SerializeUtil(Charset charset) {
        // TODO Auto-generated constructor stub
        Assert.notNull(charset);
        this.charset = charset;
    }

    @Override
    public byte[] serialize(Object object){  //序列化方法
        // TODO Auto-generated method stub
        try {
            JSONObject jsonObject = JSONObject.fromObject(object);
            String jsonString = jsonObject.toString();
            return (jsonString == null ? EMPTY_ARRAY : jsonString.getBytes(charset));
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;

    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException { //反序列化
        // TODO Auto-generated method stub
        String objectStr = null;
        Object object = null;
        if (bytes == null) {
            return object;
        }
        try {
            objectStr = new String(bytes,charset); //byte数组转换为String
            JSONObject jsonObject = JSONObject.fromObject(objectStr); //String转化为JSONObject
            object = jsonObject;  //返回的是JSONObject类型  取数据时候需要再次转换一下
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return object;
    }

}

最后如果要在Spring中使用这个序列化方法我们还需要咋redis的配置文件中注册一下。

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
<!--         键序列化方式 -->
        <property name="keySerializer">
            <bean
                class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
<!--         值序列化方式 -->
        <property name="valueSerializer">
<!--             <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> -->
<!--             <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> -->
            <bean class="com.zkxl.fep.redis.SerializeUtil"/>
        </property>
    </bean>

大功告成!这样就自定义序列化方式了。

时间: 2024-10-05 01:09:50

自定义redis序列化工具的相关文章

redis缓存工具类,提供序列化接口

1.序列化工具类 1 package com.qicheshetuan.backend.util; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.ByteArrayOutputStream; 5 import java.io.ObjectInputStream; 6 import java.io.ObjectOutputStream; 7 8 public class SerializeUtil { 9 10 //序列化 11

针对工作中的需要对已有的Java序列化工具分析

针对java原生序列化,以及优化过的java序列化工具列举说明.自己定义的类名(IOSerializable).(Fast Serialization).(FastjsonSerializable) 1.java序列化简介 序列化就是指对象通过写出描述自己状态的数值来记录自己的过程,即将对象表示成一系列有序字节,Java提供了将对象写入流和从流中恢复对象的方法.对象能包含其它的对象,而其它的对象又可以包含另外的对象.JAVA序列化能够自动的处理嵌套的对象.对于一个对象的简单域,writeObje

Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer

当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的.RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer. Spring Data JPA为我们提供了下面的Serializer:GenericToStringSerializer.Jackson2JsonRedisSeria

Spring Boot自定义Redis缓存配置,保存value格式JSON字符串

Spring Boot自定义Redis缓存,保存格式JSON字符串 部分内容转自 https://blog.csdn.net/caojidasabi/article/details/83059642 package springboot01cache.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; im

Windows环境下使用Redis缓存工具的图文详细方法

网上找了两篇关于Redis的博客,记录下! Java 使用Redis缓存工具的图文详细方法 Windows环境下使用Redis缓存工具的图文详细方法

$.ajax、$.post、from表单序列化工具

$.ajax\$.post <script type="text/javascript" language="javascript" src="js/jquery-1.8.3.js" ></script> <script type="text/javascript"> //使用Ajax($.ajax)调用 function ajax(){ $.ajax({ type:"post&q

Java 使用Redis缓存工具的图文详细方法

开始在 Java 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 Java redis 驱动,且你的机器上能正常使用 Java. (1)Java的安装配置可以参考我们的 Java开发环境配置 (2)安装了 redis 服务: 请参考:Windows环境下使用Redis缓存工具的图文详细方法 或是: 首先你需要下载驱动包,下载 jedis.jar,确保下载最新驱动包. 在你的classpath中包含该驱动包. 一.新建一个javaweb项目. 1. 新建一个Jedis的项目.

用序列化工具写入xml

标本: <?xml version="1.0" encoding="UTF-8" standalone="true"?> //文档的申明 <persons> //标签tag <person id=“18"> //ID是person标签的一个属性 <name>allen</name> <age>36</age> </person> <pe

Redis可视化工具Redis Desktop Manager使用

Redis可视化工具,RedisDesktopManager 没错,它开源的,托管在github上:https://github.com/uglide/RedisDesktopManager 还不错,下面我们使用它看看效果. 首先配置redis连接,建议加上密码,设置redis的密码百度大把的,这里我们直接修改redis.conf文件,打开它,找到# requirepass foobared (#打头的表示此行是注释说明状态,去掉#就是配置状态),我们修改下,去掉#,配置一个密码(或者干脆另起一