Spring Data Redis初步探秘

    Spring Data Redis(以下简称SDR)作为Spring Data中重要一员,基于Jedis进行了更为方便的包装,在方便Java中Redis开发的方面起到了重要的作用。SDR中也提供了模板类来进行Redis操作,而让开发者无需再过多的关注资源的获取与释放问题。

    在我上一篇文章中,介绍了我按照Spring中JDBC的方式封装Jedis。其实在那个时候我并不知道有SDR,所以就索性自己用回调的方式来写的,后面几天继续研究Redis才了解到了SDR,瞬间觉得世界好大。

一、配置

    想要在项目中正常使用SDR,需要进行简单的配置,在Spring的配置文件中分别配置RedisConnectionFactory、RedisTemplate等,即可在代码中注入使用。

1.1 配置RedisConnectionFactory

1
2
3
4
5
6
7
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.password}" />
<property name="usePool" value="true" />
<!--<property name="poolConfig" ref="" />-->
</bean>

    RedisConnection是用来与Redis交互,内容形式都是byte数组形式的二进制。

1.2 配置RedisTemplate

    RedisTemplate提供了Redis的高层级方法,对Redis的各种操作进行了抽象,用来管理数据的序列化和Redis连接等,使用RedisTemplate时,不用再去关心连接的打开和释放等问题,SDR把开发者应该考虑的重点-业务开发,突出了出来。

1
2
3
4
5
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
<property name="keySerializer" value="org.springframework.data.redis.serializer.StringRedisSerializer" />
<property name="valueSerializer" value="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</bean>

1.3 进行Redis操作

1.3.1 RedisConnection进行操作

    要通过上面配置的RedisConnectionFactory来获取JedisConnection,通过这个连接去进行Redis操作,操作完成后,再手动关闭和归还JedisConnection对象。

    此种方式的麻烦在于依然需要自己控制connection对象的获取与归还,依然会在我们实际开发中有一些羁绊。

1.3.2 RedisTemplate进行操作

    在配置完RedisTemplate后,其实就把JedisConnection的管理工作交给了SDR,这样就把开发者从繁琐的资源管理中解放了出来,可以集中精力在业务开发上。

    RedisTemplate像HibernateTemplate和JdbcTemplate一样,使用了在Spring中利用率很高的回调模式。此时可以自定义Serializer进行数据的序列化,通过回调来使用RedisConnection进行Redis操作。

    除了Redis各种数据结构的操作外的那些操作,如事务、管道、持久化等操作,也要通过RedisTemplate来执行。

1.3.3 Operational views进行操作

    SDR还提供了很多的操作视图来帮助我们更方便的使用Redis,对RedisTemplate进行了进一步的封装。此部分将在下面做介绍。

    Redis中每种数据类型都有其对应的Operations,这些Operations也可通过RedisTemplate中获得。

二、序列化

    SDR中也提供了存储内容的序列化方法,这个框架把存储到Redis中的数据都认为是bytes。SDR通过org.springframework.data.redis.serializer.RedisSerializer接口把数据进行序列化。此接口有多个实现类,均可用于Redis中key和value的序列化。但是比较常用的就是JdkSerializationRedisSerializerStringRedisSerializer

2.1 JdkSerializationRedisSerializer

    此类使用的是JDK自带的序列化机制,通过ObjectInputStream 和 ObjectOutputStream进行转换。RedisTemplate中默认开启使用预定义的序列化类,默认为此类,要使用此类进行key和value的序列化,要求被序列化的内容必须实现Serializable接口。

2.2 StringRedisSerializer

    专门对字符串类型的key和value进行序列化,根据指定的字符编码进行字符串的转换,默认编码为UTF-8。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public StringRedisSerializer() {
this(Charset.forName("UTF8"));
}
public StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
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));
}

2.3 GenericToStringSerializer

    此类也可以实现对象和字节数组间的转换,但是要依赖于Spring中的ConversionService,进行对象和String之间的转换,然后再进行String与字节数组间的转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public T deserialize(byte[] bytes) {
if (bytes == null) {
return null;
}
String string = new String(bytes, charset);
return converter.convert(string, type);
}
public byte[] serialize(T object) {
if (object == null) {
return null;
}
String string = converter.convert(object, String.class);
return string.getBytes(charset);
}

2.4 Jackson2JsonRedisSerializer 和 JacksonJsonRedisSerializer

    这两个类内部都使用了jackson进行对象与json之间的转换,使用的时候需要指定对象的Class类型,也都是泛型类,需要指定对象的类型。其实这两个类的内部是一样的,只是在1.7版本后就废弃了JacksonJsonRedisSerializer,而使用Jackson2JsonRedisSerializer。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public JacksonJsonRedisSerializer(Class<T> type) {
this.javaType = getJavaType(type);
}
public JacksonJsonRedisSerializer(JavaType javaType) {
this.javaType = javaType;
}
public T deserialize(byte[] bytes) throws SerializationException {
if (SerializationUtils.isEmpty(bytes)) {
return null;
}
try {
return (T) this.objectMapper.readValue(bytes, 0, bytes.length, javaType);
} catch (Exception ex) {
throw new SerializationException("Could not read JSON: " + ex.getMessage(), ex);
}
}
public byte[] serialize(Object t) throws SerializationException {
if (t == null) {
return SerializationUtils.EMPTY_ARRAY;
}
try {
return this.objectMapper.writeValueAsBytes(t);
} catch (Exception ex) {
throw new SerializationException("Could not write JSON: " + ex.getMessage(), ex);
}
}

2.5 GenericJackson2JsonRedisSerializer

    此类与Jackson2JsonRedisSerializer内部的序列化和反序列化基本类似,方法都一样,只不过GenericJackson2JsonRedisSerializer是通用类型的,并非泛型类。

2.6 OxmSerializer

    执行xml与对象间的序列化和反序列化,有多种实现方式。几乎没用过,也没怎么去研究过。

三、Operations

    RedisTemplate提供了操作视图的方式来执行Redis动作,在各种Operations中仍然是用到了RedisTemplate中执行Redis时的回调方式,即对RedisTemplate中的多种API进行了封装,以方便开发者来使用。

3.1 初探SDR的Operations

&emssp;   对于Redis中各种类型的数据也都有对应的Operations来操作,下面的表格引用自SDR的官方文档,自己简单翻译了一下。

接口 描述
ValueOperations string类型数据的操作
ListOperations list数据操作
SetOperations set数据操作
ZSetOperations sorted set数据操作
HashOperations hash数据操作
HyperLogLogOperations HyperLogLog操作
GeoOperations 地理空间操作

    上面这个表格里各个接口中的方法,都可以自由指定keyvalue,而下面这个表格中就是已经绑定好key,直接进行对应操作即可。

接口 描述
BoundValueOperations string类型数据的操作
BoundListOperations list数据操作
BoundSetOperations set数据操作
BoundZSetOperations sorted set数据操作
BoundHashOperations hash数据操作
BoundGeoOperations 地理空间操作操作

3.2 源码观察Operations

SDR中提供的Operations需要通过RedisTemplate来产生,都通过bound类型OpsopsFor类型来产生Bound类型Operations类型Operations,类型都是上面表格中写的那些。

    下面这段代码中,展示了RedisTemplate中获取绑定和非绑定Operations的源码。

1
2
3
4
5
6
7
8
9
10
public BoundValueOperations<K, V> boundValueOps(K key) {
return new DefaultBoundValueOperations<K, V>(key, this);
}
public ValueOperations<K, V> opsForValue() {
if (valueOps == null) {
valueOps = new DefaultValueOperations<K, V>(this);
}
return valueOps;
}

    目前,SDR中提供的都是各种接口的默认实现,并没有给定自定义实现Operations的接口的方式。

3.3 Operations简单使用

    下面的代码中是Operations使用的举例,用最简单的方式来编写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private RedisTemplate redisTemplate;
/**
* 非绑定形式的Operation设值和取值
* @param key
* @param value
*/
public void setStringValueSample(String key, String value){
/*
* 非绑定key的形式下需要指定key
*/
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value);
valueOperations.get(key);
}
/**
* 绑定形式的Operation设置和取值
* @param key
* @param value
*/
public void setBoundSample(String key, String value){
/*
* 绑定形式下,在获取Operations时,就指定好key,后续操作无需再指定key
*/
BoundValueOperations<String, String> boundValueOperations = redisTemplate.boundValueOps(key);
boundValueOperations.set(value);
boundValueOperations.get();
}

    这篇文章是我对于Spring Data Redis的一个认识总结,简要概括了我对于这个小型框架的了解。其实对于Redis来讲,它的源码本身就不多,很多人也都积极去研究源码,这对于我们深刻了解和使用有很大帮助。对于Java的开发者们来说,Jedis源码也没那么多,再到Spring Data Redis逻辑也没有太过于复杂和繁多,所以,不管是在学习的时候还是在工作的时候,多看看源码,整体做掌握还是很有帮助的。

    本篇博客是我初次基础SDR所写,如果有不正确或者缺漏的地方,还望大家予以指正和补充,一起学习。

大爷给小弟的零花钱
显示 Gitment 评论