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>

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

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

  1. SpringBoot缓存管理(三) 自定义Redis缓存序列化机制

    前言 在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理. 接下来 ...

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

    1.序列化工具类 package com.qicheshetuan.backend.util; import java.io.ByteArrayInputStream; import java.io. ...

  3. Redis序列化存储Java集合List等自定义类型

    在"Redis学习总结和相关资料"http://blog.csdn.net/fansunion/article/details/49278209 这篇文章中,对Redis做了总体的 ...

  4. SpringBoot修改Redis序列化方式

    前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...

  5. 用c#开发微信(5)自定义菜单设置工具 (在线创建)

    读目录 1 使用 2 原理 3. 错误 上次写了<用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)>,有园友问到如何创建菜单的问题,今天就介绍下 ...

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

    Spring Boot自定义Redis缓存,保存格式JSON字符串 部分内容转自 https://blog.csdn.net/caojidasabi/article/details/83059642 ...

  7. Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer

    当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的.RedisTemplate默认使用的是JdkSerializat ...

  8. 最全的Java操作Redis的工具类,使用StringRedisTemplate实现,封装了对Redis五种基本类型的各种操作!

    转载自:https://github.com/whvcse/RedisUtil 代码 ProtoStuffSerializerUtil.java import java.io.ByteArrayInp ...

  9. spring-data-redis注册fastjson序列化工具

    使用spring-data-redis的时候,其序列化工具自带:

随机推荐

  1. 程序猿职业生涯中的 Norris 常数

    我的朋友Clift Norris发现了一个基本常数.我称之为Norris常数,一个未经培训的程序猿在他或她遇到瓶颈之前能写出的平均代码量.Clift预计这个值是1500行. 超过这个数以后,代码会变得 ...

  2. [py]django表单不清空实现的2种方法

    参考 参考: django实现内容不清空2种方法 django form的作用 1.生成html标签 2.验证输入内容 form生成表单 zhuji/forms.py - 实例化表单 - 定制form ...

  3. 微信小程序性能测试之jmeter踩坑秘籍(前言)

    最近要做个微信小程序的性能压测,虽然之前只做过web端的,但想一想都是压后端的接口,所以果断答应了下来,之前对jmeter都是小打小闹,所以趁着这次机会好好摆弄摆弄. ---------------- ...

  4. (转载)【cocos2dx 3.x Lua] 注册事件函数详解

    出处: http://www.2cto.com/kf/201409/338235.html coocs2dx 版本 3.1.1 registerScriptTouchHandler 注册触屏事件 re ...

  5. Apache下设置网站目录的访问权限

    禁止用户对某一个目录及目录下文件的访问,仅允许本地访问 <Directory "/wwwroot/cert/"> Require local </Director ...

  6. linux下操作iso文件的两个shell程序

    记得这还是当初玩cdlinux时弄的,当初应该是由于windows下的Ultraiso对cdlinux的镜像修改后导致镜像无法引导,所以就使用linux下的命令进行操作 这应该是挂载iso文件的命令: ...

  7. Linux基础命令---显示文本grep

    grep 按照指定的模式,在文件中搜索匹配的行,将结果显示在标准输出.另外还有两个指令egrep相当于grep –E,fgrep相当于grep -F.如果没有给出文件名,那么从标准输入读取. 此命令的 ...

  8. 使用echo命令清空tomcat日志文件

    使用echo命令清空日志文件echo -n "" > /server/tomcat/logs/catalina.out ==>要加上"-n"参数,默 ...

  9. python之路----模块与序列化模块

    认识模块 什么是模块 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀. 但其实import加载的模块分为四个通用类别: 1 使用pyt ...

  10. 2D 2 3D 开源项目

    http://www.cvlibs.net/projects.php http://www.cvlibs.net/software/libelas/