一.简介
简单的说,RedisTemplate
和StringRedisTemplate
的关系如下:
1.
StringRedisTemplate
是RedisTemplate
的子类。2.
StringRedisTemplate
的各个序列化策略都是StringRedisSerializer
,而RedisTemplate
用的是JdkSerializationRedisSerializer
。
二.RedisTemplate和StringRedisTemplate的代码结构
从RedisTemplate
类说起。
在RedisTemplate
类中,定义了这样四个变量:
1 |
|
分别代表了普通key
,value
,和Hash
类型的key
,value的序列化策略,可以分别设置。
另外定义变量,用来指定默认的序列化策略:
1 |
|
在RedisTemplate
类中,定义了afterPropertiesSet()
方法,当Spring
创建RedisTemplate
类的对象时,会调用这个方法:
1 | public void afterPropertiesSet() { |
可以看到,在默认情况下,RedisTemplate
使用的默认序列化策略是JdkSerializationRedisSerializer
。包括RedisTemplate
下的key
,value
,hash-key
,hash-value的序列化,都用这种策略。
再来看看StringRedisTemplate
,他作为RedisTemplate的子类,只是修改了序列化策略:
1 | public class StringRedisTemplate extends RedisTemplate<String, String> { |
以上就是StringRedisTemplate
整个类的内容,可以看到,在他的默认构造中,key
,value
,hash-key
,hash-value
都使用的是StringRedisSerializer类作为序列化策略。这也就是StringRedisTemplate
和他的父类RedisTemplate的主要区别。
三.序列化策略
更进一步,看一下这个序列化策略是什么。
上面提到的StringRedisSerializer
和JdkSerializationRedisSerializer
都是序列化策略类,他们都实现了一个RedisSerializer<T>
接口:
1 | public interface RedisSerializer<T> { |
接口表达的意思很简单,两个方法,serialize用于序列化,把对象变为byte
数组,deserialize
用于反序列化,把byte数组转为对象。
StringRedisSerializer
看看StringRedisSerializer
是怎么做的:
1.StringRedisSerializer的构造:
1 | public StringRedisSerializer() { |
定义了编码格式,默认UTF_8
。
2.StringRedisSerializer的serialize和deserialize方法:
1 | public String deserialize(@Nullable byte[] bytes) { |
可以看到,StringRedisSerializer
采用的是字符串和对应编码下二进制数组之间的转换。
在这种编码格式下,如果我们向redis保存信息,然后用客户端访问Redis
时,只要编码格式一致,就能看到保存信息的原文。保存字符串ABC
,客户端看到的也是字符串ABC
。
JdkSerializationRedisSerializer
然后对比看看JdkSerializationRedisSerializer
是怎么做的。
1.JdkSerializationRedisSerializer的构造:
1 | private final Converter<Object, byte[]> serializer; |
可以看到,JdkSerializationRedisSerializer
定义了两个变量,serializer和deserializer
,显然是用来序列化和反序列化的,他们两个的类型是一样的,都是Converter接口,只是泛型不同。
Converter接口:
1 |
|
就一个方法。
另外在JdkSerializationRedisSerializer
的构造中,对serializer和deserializer
进行了初始化,使用SerializingConverter
和DeserializingConverter作为实现类。
2.JdkSerializationRedisSerializer的serialize和deserialize方法:
1 | public Object deserialize(@Nullable byte[] bytes) { |
其实就是调用了对应Converter
的convert
方法。
3.关于Converter
既然到这了,就再深入一步,看看SerializingConverter
和DeserializingConverter的convert
方法。
首先,序列化:
SerializingConverter
的相关方法,贴一部分关键的:
1 | public SerializingConverter() { |
可以看到,SerializingConverter
类定义了serializer
变量,用DefaultSerializer类实现,序列化的方式是调用DefaultSerializer
的serialize
方法:
1 |
|
DefaultSerializer
的serialize
方法使用了ObjectOutputStream
,调用writeObject方法序列化对象。
对应的,反序列化:
DeserializingConverter
的convert方法,贴一下相关代码:
1 | public DeserializingConverter() { |
可见DeserializingConverter
使用了DefaultDeserializer作为反序列化工具,调用了他的deserialize方法:
1 |
|
对比SerializingConverter
,DeserializingConverter
使用的是ConfigurableObjectInputStream
,并调用他的readObject
方法进行反序列化。
这种序列化方式,如果保存信息至redis
,用客户端查看时,保存的信息看起来像是在原来的字符前面加了几个字符。
比如:
1 | JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(); |
这种情况下,得到的byte
数组是:
jdkByteArr
:
{-84,-19,0,5,116,0,10,67,83,68,78,-27,-115,-102,-27,-82,-94}
stringByteArr:
{67,83,68,78,-27,-115,-102,-27,-82,-94}
StringRedisSerializer
把字符串本身转化成byte
数组,而JdkSerializationRedisSerializer
在数组前面加了几个字符,这些字符也会被保存到redis中。
所以,从数据上来说,这两种序列化策略处理的数据是不会共通的,各人管各人的。
四.关于redisTemplate的Operations
使用redisTemplate
时,除了调用execute
方法并自定义RedisCallback之外,还可以使用redisTemplate
提供的几个Operations接口。
redisTemplate
中定义了以下几个Operations
:
1 |
|
这几个Operations
接口,分别提供了对不同种类数据的操作方法。
以ValueOperations
为例,他提供的方法有:
1 | void set(K var1, V var2); |
其他的Operations
提供的方法各有不同,但是这些Operations
的使用方式都是相同的。
不同的Operations分别通过RedisTemplate
的以下方法获取:
1 | public ValueOperations<K, V> opsForValue() { |
可见,在这些获得Operations
的方法中,都提供了一个默认实现类,并且把RedisTemplate
对象本身当做参数传给了这个实现类。
还是以ValueOperations
为例,RedisTemplate
提供的默认实现类是DefaultValueOperations,看看这个类的源码:
1 | package org.springframework.data.redis.core; |
所有Operations
实现类都是AbstractOperations
的子类,另外各自实现各自的接口。
实现类的方法中多数都是调用了this.execute()
方法,这个方法在父类AbstractOperations中,最终调用的其实也是RedisTemplate
的execute()
方法。
以上面DefaultValueOperations
的set()
方法为例,看一下代码:
1 | public void set(K key, V value) { |
首先是对value
的处理,调用this.rawValue()
方法,把value序列化成byte
数组,这个方法在父类AbstractOperations
中:
1 | byte[] rawValue(Object value) { |
可见,代码用的是自己的valueSerializer
来序列化value
,这个valueSerializer来自RedisTemplate
。
回到set()
方法,value
序列化完成后,调用this.execute()
方法,给此方法传递的第一个参数是:
1 | new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key) { |
这个参数实际上是一个ValueDeserializingRedisCallback
对象,在其中定义了inRedis()
方法的实现。
this.execute()
方法在父类AbstractOperations中:
1 |
|
其中this.template
指的就是初始化时传入的RedisTemplate
,其execute()方法是这样的:
1 |
|
然后调用下面的方法:
1 |
|
方法初始化了RedisConnection
,最后面调用了RedisCallback
的doInRedis()
方法,也就是这一行:
1 | T result = action.doInRedis(connToExpose); |
这里的变量action
就是在set()
方法中自定义的new AbstractOperations<K, V>.ValueDeserializingRedisCallback(key)
。
ValueDeserializingRedisCallback
类是AbstractOperations
的内部抽象类,他的doInRedis()
方法是这样的:
1 | public final V doInRedis(RedisConnection connection) { |
可见调用了inRedis()
方法,其第一个参数是序列化后的key
,调用的是AbstractOperations
的rawKey()
方法,代码如下:
1 | byte[] rawKey(Object key) { |
这里把key
进行序列化,keySerializer()
方法从RedisTemplate
中获取keySerializer
,并由keySerializer
对key
进行序列化。
在ValueDeserializingRedisCallback
类中的inRedis()
方法是抽象方法,具体的实现在DefaultValueOperations
的set()
方法中,也就是这一部分:
1 | protected byte[] inRedis(byte[] rawKey, RedisConnection connection) { |
最终调用的是RedisConnection
的set()
方法,完成Redis
的set
操作。
以上就是在RedisTemplate中使用ValueOperations
进行set
操作的全部代码流程。
对Redis
的不同操作分散在RedisTemplate
的不同Operations中,只是调用的方法不同,调用流程都差不多。
参考地址
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员微信号:chengcheng222e
,他会拉你们进群。