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

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

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

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

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

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

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

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

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

默认的序列化方式为JdkSerializationRedisSerializer。

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

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

1、StringRedisSerializer

  1. public class StringRedisSerializer implements RedisSerializer<String> {
  2.  
  3. private final Charset charset;
  4.  
  5. public StringRedisSerializer() {
  6. this(Charset.forName("UTF8"));
  7. }
  8.  
  9. public StringRedisSerializer(Charset charset) {
  10. Assert.notNull(charset);
  11. this.charset = charset;
  12. }
  13.  
  14. public String deserialize(byte[] bytes) {
  15. return (bytes == null ? null : new String(bytes, charset));
  16. }
  17.  
  18. public byte[] serialize(String string) {
  19. return (string == null ? null : string.getBytes(charset));
  20. }
  21. }

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

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

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

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

2、JdkSerializationSerializer

  1. public class JdkSerializationRedisSerializer implements RedisSerializer<Object> {
  2.  
  3. private Converter<Object, byte[]> serializer = new SerializingConverter();
  4. private Converter<byte[], Object> deserializer = new DeserializingConverter();
  5.  
  6. public Object deserialize(byte[] bytes) {
  7. if (SerializationUtils.isEmpty(bytes)) {
  8. return null;
  9. }
  10.  
  11. try {
  12. return deserializer.convert(bytes);
  13. } catch (Exception ex) {
  14. throw new SerializationException("Cannot deserialize", ex);
  15. }
  16. }
  17.  
  18. public byte[] serialize(Object object) {
  19. if (object == null) {
  20. return SerializationUtils.EMPTY_ARRAY;
  21. }
  22. try {
  23. return serializer.convert(object);
  24. } catch (Exception ex) {
  25. throw new SerializationException("Cannot serialize", ex);
  26. }
  27. }
  28. }

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

下面是serializer.convert方法的源码

  1. public byte[] convert(Object source) {
  2. ByteArrayOutputStream byteStream = new ByteArrayOutputStream(256);
  3. try {
  4. this.serializer.serialize(source, byteStream);
  5. return byteStream.toByteArray();
  6. }
  7. catch (Throwable ex) {
  8. throw new SerializationFailedException("Failed to serialize object using " +
  9. this.serializer.getClass().getSimpleName(), ex);
  10. }
  11. }

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

  1. public class DefaultSerializer implements Serializer<Object> {
  2.  
  3. /**
  4. * Writes the source object to an output stream using Java Serialization.
  5. * The source object must implement {@link Serializable}.
  6. */
  7. public void serialize(Object object, OutputStream outputStream) throws IOException {
  8. if (!(object instanceof Serializable)) {
  9. throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +
  10. "but received an object of type [" + object.getClass().getName() + "]");
  11. }
  12. ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
  13. objectOutputStream.writeObject(object);
  14. objectOutputStream.flush();
  15. }
  16.  
  17. }

从上面的代码可以看到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类型。

下面是我的代码。

  1. package com.zkxl.fep.redis;
  2.  
  3. import java.nio.charset.Charset;
  4.  
  5. import org.springframework.data.redis.serializer.RedisSerializer;
  6. import org.springframework.data.redis.serializer.SerializationException;
  7. import org.springframework.util.Assert;
  8.  
  9. import net.sf.json.JSONObject;
  10.  
  11. public class SerializeUtil implements RedisSerializer<Object>{
  12.  
  13. static final byte[] EMPTY_ARRAY = new byte[0];
  14. private final Charset charset;
  15.  
  16. public SerializeUtil() {
  17. // TODO Auto-generated constructor stub
  18. this(Charset.forName("UTF8"));
  19. }
  20.  
  21. public SerializeUtil(Charset charset) {
  22. // TODO Auto-generated constructor stub
  23. Assert.notNull(charset);
  24. this.charset = charset;
  25. }
  26.  
  27. @Override
  28. public byte[] serialize(Object object){ //序列化方法
  29. // TODO Auto-generated method stub
  30. try {
  31. JSONObject jsonObject = JSONObject.fromObject(object);
  32. String jsonString = jsonObject.toString();
  33. return (jsonString == null ? EMPTY_ARRAY : jsonString.getBytes(charset));
  34. } catch (Exception e) {
  35. // TODO: handle exception
  36. e.printStackTrace();
  37. }
  38. return null;
  39.  
  40. }
  41.  
  42. @Override
  43. public Object deserialize(byte[] bytes) throws SerializationException { //反序列化
  44. // TODO Auto-generated method stub
  45. String objectStr = null;
  46. Object object = null;
  47. if (bytes == null) {
  48. return object;
  49. }
  50. try {
  51. objectStr = new String(bytes,charset); //byte数组转换为String
  52. JSONObject jsonObject = JSONObject.fromObject(objectStr); //String转化为JSONObject
  53. object = jsonObject; //返回的是JSONObject类型 取数据时候需要再次转换一下
  54. } catch (Exception e) {
  55. // TODO: handle exception
  56. e.printStackTrace();
  57. }
  58. return object;
  59. }
  60.  
  61. }

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

  1. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
  2. <property name="connectionFactory" ref="connectionFactory" />
  3. <!-- 键序列化方式 -->
  4. <property name="keySerializer">
  5. <bean
  6. class="org.springframework.data.redis.serializer.StringRedisSerializer" />
  7. </property>
  8. <!-- 值序列化方式 -->
  9. <property name="valueSerializer">
  10. <!-- <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> -->
  11. <!-- <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> -->
  12. <bean class="com.zkxl.fep.redis.SerializeUtil"/>
  13. </property>
  14. </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. 我读过的最好的epoll讲解(转)

    原文:http://zhihu.com/question/20122137/answer/14049112 作者:蓝形参来源:知乎 首先我们来定义流的概念,一个流可以是文件,socket,pipe等等 ...

  2. seajs引入jquery为什么无效?

    简单来说,你的jquery没有模块化? 答案:传送门1.传送门2

  3. kettle中源和目标表结构不一致的情况处理

    创建数据仓库的过程中,往往会遇到这样的问题,例如:源表由于业务原因新增了字段,而ETL程序中是按照之前的源表结构进行抽取的,那么如果不重新构建ETL程序,新的指标就不会流入DW,问题如下图所示 创建了 ...

  4. 从xtraback 备份文件中 单独恢复一张 innodb 表

    从xtraback 备份文件中 单独恢复一张 innodb 表 http://blog.sina.com.cn/s/blog_445e807b0101dbgw.html 能够恢复一张表的前提是独立表空 ...

  5. 十天精通CSS3(12)

    自由缩放属性resize 为了增强用户体验,CSS3增加了很多新的属性,其中resize就是一个重要的属性,它允许用户通过拖动的方式来修改元素的尺寸来改变元素的大小.到目前为止,可以使用overflo ...

  6. yum && 编译 安装mysql 5.7 多实例

    yum安装 [root@localhost ~]# wget http://repo.mysql.com/mysql57-community-release-el7.rpm [root@localho ...

  7. SV搭建验证环境

    1)首先定义纯虚类Sv_object,主要实现下边两个function: 定义local static 变量nextobjectID; 虚方法 virtual function void copy(S ...

  8. Object-C-系统类型对象归档

    系统类型主要是指NSString NSDictionary,NSArray,NSData,NSNumber 类型数据(包括对应可变类型); 这些类型已经实现了NSCoding协议,支持归档, 写入方法 ...

  9. python socket编程函数介绍

    网上看到一个socket中常用函数的介绍,记录一下 https://blog.csdn.net/rebelqsp/article/details/22109925

  10. IO(字节流)

    1. 字节流类以InputStream 和 OutputStream为顶层类,他们都是抽象类(abstract) 2. 最重要的两种方法是read()和write(),它们分别对数据的字节进行读写.两 ...