自定义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>
大功告成!这样就自定义序列化方式了。
自定义redis序列化工具的更多相关文章
- SpringBoot缓存管理(三) 自定义Redis缓存序列化机制
前言 在上一篇文章中,我们完成了SpringBoot整合Redis进行数据缓存管理的工作,但缓存管理的实体类数据使用的是JDK序列化方式(如下图所示),不便于使用可视化管理工具进行查看和管理. 接下来 ...
- redis缓存工具类,提供序列化接口
1.序列化工具类 package com.qicheshetuan.backend.util; import java.io.ByteArrayInputStream; import java.io. ...
- Redis序列化存储Java集合List等自定义类型
在"Redis学习总结和相关资料"http://blog.csdn.net/fansunion/article/details/49278209 这篇文章中,对Redis做了总体的 ...
- SpringBoot修改Redis序列化方式
前言 由于Springboot默认提供了序列化方式并不是非常理想,对于高要求的情况下,序列化的速度和序列化之后大小有要求的情况下,不能满足,所以可能需要更换序列化的方式. 这里主要记录更换序列化的方式 ...
- 用c#开发微信(5)自定义菜单设置工具 (在线创建)
读目录 1 使用 2 原理 3. 错误 上次写了<用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)>,有园友问到如何创建菜单的问题,今天就介绍下 ...
- Spring Boot自定义Redis缓存配置,保存value格式JSON字符串
Spring Boot自定义Redis缓存,保存格式JSON字符串 部分内容转自 https://blog.csdn.net/caojidasabi/article/details/83059642 ...
- Redis 序列化方式StringRedisSerializer、FastJsonRedisSerializer和KryoRedisSerializer
当我们的数据存储到Redis的时候,我们的键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的.RedisTemplate默认使用的是JdkSerializat ...
- 最全的Java操作Redis的工具类,使用StringRedisTemplate实现,封装了对Redis五种基本类型的各种操作!
转载自:https://github.com/whvcse/RedisUtil 代码 ProtoStuffSerializerUtil.java import java.io.ByteArrayInp ...
- spring-data-redis注册fastjson序列化工具
使用spring-data-redis的时候,其序列化工具自带:
随机推荐
- 我读过的最好的epoll讲解(转)
原文:http://zhihu.com/question/20122137/answer/14049112 作者:蓝形参来源:知乎 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等 ...
- seajs引入jquery为什么无效?
简单来说,你的jquery没有模块化? 答案:传送门1.传送门2
- kettle中源和目标表结构不一致的情况处理
创建数据仓库的过程中,往往会遇到这样的问题,例如:源表由于业务原因新增了字段,而ETL程序中是按照之前的源表结构进行抽取的,那么如果不重新构建ETL程序,新的指标就不会流入DW,问题如下图所示 创建了 ...
- 从xtraback 备份文件中 单独恢复一张 innodb 表
从xtraback 备份文件中 单独恢复一张 innodb 表 http://blog.sina.com.cn/s/blog_445e807b0101dbgw.html 能够恢复一张表的前提是独立表空 ...
- 十天精通CSS3(12)
自由缩放属性resize 为了增强用户体验,CSS3增加了很多新的属性,其中resize就是一个重要的属性,它允许用户通过拖动的方式来修改元素的尺寸来改变元素的大小.到目前为止,可以使用overflo ...
- yum && 编译 安装mysql 5.7 多实例
yum安装 [root@localhost ~]# wget http://repo.mysql.com/mysql57-community-release-el7.rpm [root@localho ...
- SV搭建验证环境
1)首先定义纯虚类Sv_object,主要实现下边两个function: 定义local static 变量nextobjectID; 虚方法 virtual function void copy(S ...
- Object-C-系统类型对象归档
系统类型主要是指NSString NSDictionary,NSArray,NSData,NSNumber 类型数据(包括对应可变类型); 这些类型已经实现了NSCoding协议,支持归档, 写入方法 ...
- python socket编程函数介绍
网上看到一个socket中常用函数的介绍,记录一下 https://blog.csdn.net/rebelqsp/article/details/22109925
- IO(字节流)
1. 字节流类以InputStream 和 OutputStream为顶层类,他们都是抽象类(abstract) 2. 最重要的两种方法是read()和write(),它们分别对数据的字节进行读写.两 ...