问题 & 方案
1.1 问题
RedisTemplate 是 Spring Boot 访问 Redis 的核心组件,底层通过 RedisConnectionFactory 对多种 Redis 驱动进行集成,上层通过 XXXOperations 提供丰富的 API ,并结合 Spring4 基于泛型的 bean 注入,极大的提供了便利,成为日常开发的一大利器。
但美中不足的是,需要针对应用场景,使用不同的 RedisSerializer 对RedisTemplate 进行定制,那有没有一种方法能够自动完成 RedisTemplate 的定义呢?
1.2 方案
如果通过注解完成 RedisSerializer 的配置,在 Spring 启动时,对 RedisTemplate 进行自动注册,会是怎样呢?
大概的思路如下:
- 自定义一套注解,通过注解完成 RedisTemplate 的 RedisSerializer 配置;
- 在 Spring 启动时,对托管 bean 进行功能加强,自动生成 RedisTemplate ;
- 自定义 @EnableXXX 注解,完成与 Spring Boot 的集成。
OK,让我们一起开启这次旅行。
基础知识准备
在正式实战前,让我们一起对所需的技术点进行梳理。
所涉及的技术点包括:
- RedisTemplate 简介;
- Spring Boot 对 RedisTemplate 的支持;
- Spring Bean 生命周期;
- Spring BeanPostProcessor 扩展点。
2.1 RedisTemplate 简介
XXXTemplate 是 Spring 的一大设计特色,对 Redis 的支持同样如此, RedisTemplate 即为与 Redis 交互的核心类。
2.1.1 RedisTemplate 继承结构
RedisTemplate 继承自 RedisAccessor , 实现 RedisOperations 和 BeanClassLoaderAware 两个接口。
RedisAccessor 比较简单,主要完成 RedisConnectionFactory 的管理,并对其进行非空校验,核心代码如下:
public class RedisAccessor implements InitializingBean {
protected final Log logger = (getClass());
private @Nullable RedisConnectionFactory connectionFactory;
// 对connectionFactory进行非空检测
public void afterPropertiesSet() {
(getConnectionFactory() != null, "RedisConnectionFactory is required");
}
@Nullable
public RedisConnectionFactory getConnectionFactory() {
return connectionFactory;
}
public RedisConnectionFactory getRequiredConnectionFactory() {
RedisConnectionFactory connectionFactory = getConnectionFactory();
if (connectionFactory == null) {
throw new IllegalStateException("RedisConnectionFactory is required");
}
return connectionFactory;
}
public void setConnectionFactory(RedisConnectionFactory connectionFactory) {
= connectionFactory;
}
}
BeanClassLoaderAware 接口为 Spring 的扩展接口,主要完成 ClassLoader 的注入:
// Aware 为标记接口,内部没有任何方法
public interface BeanClassLoaderAware extends Aware {
void setBeanClassLoader(ClassLoader classLoader);
}
RedisOperations 接口,对 Redis 操作进行封装,是这次分析的重点。
2.1.2 RedisOperations
RedisOperations 接口对 Redis 操作进行封装。
RedisOperations 接口中的方法根据功能大概可以分为以下几类:
- execute 模板方法,提供底层 API 操作。通过该方法可以直接访问 RedisConnection 对象;
- key 相关方法,直接提供 Redis key 相关操作;
- opsForXXX 相关方法,XXXOperations 是 RedisTemplate 对 Redis 数据结构操作的一种高级 API ,这是我们最常使用的接口;
- serializer 相关方法,对 RedisTemplate 内部序列化方式进行定制;
- 其他方法,包括 killClient 、 slaveOf 等,在业务开发中,很少用到。
RedisOperations 核心方法具体如下:
方法
含义
<T> T execute(RedisCallback<T> action)
处理基于 RedisConnection 的回调
<T> T execute(SessionCallback<T> session)
处理基于 RedisOperations 的回调
List<Object> executePipelined(RedisCallback<?> action)
处理基于 RedisConnection 的 pipeline 回调
List<Object> executePipelined(final SessionCallback<?> session)
基于 RedisOperations 的 pipeline 的回调
<T> T execute(RedisScript<T> script, List<K> keys, Object… args)
支持 lua 脚本支持
Boolean hasKey(K key)
key 是否存在
Long countExistingKeys(Collection<K> keys)
redis 中存在 keys 的数量
Boolean delete(K key)
删除 key
Long delete(Collection<K> keys)
批量删除 keys
Boolean unlink(K key)
异步删除 key
Long unlink(Collection<K> keys)
批量异步删除 keys
DataType type(K key)
获取 key 的类型
Set<K> keys(K pattern)
符合正则 pattern 的 key
K randomKey()
随机获取 key
void rename(K oldKey, K newKey)
重命名 key
Boolean renameIfAbsent(K oldKey, K newKey)
如果存在,重命名 key
Boolean expire(K key, long timeout, TimeUnit unit)
为 key 设置过期时间
Boolean expireAt(K key, Date date)
为 key 设置过期时间
Boolean persist(K key)
移除 key 的过期时间,使其变为永久
Boolean move(K key, int dbIndex)
将 key 移动到 dbIndex 中
Long getExpire(K key)
获取 key 的过期时间
Long getExpire(K key, TimeUnit timeUnit);
获取 key 的过期时间
List<V> sort(SortQuery<K> query)
对集合进行排序
List<RedisClientInfo> getClientList()
获取 client 链接
void killClient(String host, int port)
kill 客户端
void slaveOf(String host, int port)
调整 master 节点
void slaveOfNoOne()
与 master 断开链接
ClusterOperations<K, V> opsForCluster()
获取 ClusterOperations
GeoOperations<K, V> opsForGeo()
获取 GeoOperations
BoundGeoOperations<K, V> boundGeoOps(K key)
获取 BoundGeoOperations
<HK, HV> HashOperations<K, HK, HV> opsForHash()
获取 HashOperations
<HK, HV> BoundHashOperations<K, HK, HV> boundHashOps(K key)
获取 BoundHashOperations
HyperLogLogOperations<K, V> opsForHyperLogLog()
获取 HyperLogLogOperations
ListOperations<K, V> opsForList()
获取 ListOperations
BoundListOperations<K, V> boundListOps(K key)
获取 BoundListOperations
SetOperations<K, V> opsForSet()
获取 SetOperations
BoundSetOperations<K, V> boundSetOps(K key)
获取 BoundSetOperations
ValueOperations<K, V> opsForValue()
获取 ValueOperations
BoundValueOperations<K, V> boundValueOps(K key)
获取 BoundValueOperations
ZSetOperations<K, V> opsForZSet()
获取 ZSetOperations
BoundZSetOperations<K, V> boundZSetOps(K key)
获取 BoundZSetOperations
RedisSerializer<?> getKeySerializer()
获取 key RedisSerializer
RedisSerializer<?> getValueSerializer()
获取 value RedisSerializer
RedisSerializer<?> getHashKeySerializer()
获取 hash key RedisSerializer
RedisSerializer<?> getHashValueSerializer()
获取 hash value RedisSerializer
备注:以上表格并非全部方法,对重置方法进行了忽略,但不影响对 RedisOperations 的分析。
针对 RedisOperations 接口,有几个注意点:
- 大量用到泛型类型,这些类型如何进行序列化和反序列化;
- 大量的 XXXOperations 和 BoundXXXOperations 究竟有什么区别。
让我们带着疑问去了解下 RedisTempalte 。
2.1.3 RedisTempalte
RedisTemplate 实现 RedisOperations,是 Spring 与 Redis 交互的核心类。
2.1.3.1 RedisSerializer
RedisSerializer 主要处理 Redis 序列化与反序列化。
RedisSerializer 接口如下:
public interface RedisSerializer<T> {
// 对象序列化
byte[] serialize(T t) throws SerializationException;
// 对象反序列化
T deserialize(byte[] bytes) throws SerializationException;
}
在 RedisTemplate 中存在几个 RedisSerializer 字段,具体如下:
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
......
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
......
序列化器
作用
keySerializer
处理 key 的序列化和反序列化
valueSerializer
处理 value 的序列化和反序列化
hashKeySerializer
处理 hash 类型 key 的序列化和反序列化
hashValueSerializer
处理 hash 类型 value 的序列化和反序列化
从 RedisTemplate 的设计中可以得出,对于不同类型的 key、 value 需要设置不同的 RedisSerializer。
针对 RedisSerializer, Spring 提供了一组实现,具体如下:
实现类
含义
OxmSerializer
基于 Spring O/X Mapping 的序列化器
GenericToStringSerializer
基于 ConversionService 到 String 的序列化器
GenericJackson2JsonRedisSerializer
基于 Jackson 的通用类型序列化器
StringRedisSerializer
基于 String 的序列化器
JdkSerializationRedisSerializer
基于 jdk 序列化的序列化器
Jackson2JsonRedisSerializer
基于 Jackson 的特定类型序列化器
一般情况下,Spring 的自带实现已经能够满足大多数需求。如果对性能有更高追求,可以自行扩展。
2.1.3.2 XXXOperations VS BoundXXXOperations
XXXOperations 和 BoundXXXOperations 大多情况下是成对出现,两个有区别,也有联系。
RedisTemplate 把 Redis 的数据结构封装为 XXXOperations 和 BoundXXXOperations,以区分不同数据结构,使每个数据结构成为一个内聚的整体。
Operations 体系包括:
类型
Operations
BoundOperations
Cluster
ClusterOperations
无
HyperLogLog
HyperLogLogOperations
无
geo
GeoOperations
BoundGeoOperations
Hash
HashOperations
BoundHashOperations
List
ListOperations
BoundListOperations
Set
SetOperations
BoundSetOperations
Key Value
ValueOperations
BoundValueOperations
ZSet
ZSetOperations
BoundZSetOperations
XXXOperations 和 BoundXXXOperations 都是对 XXX 数据结构的封装,都提供操作该数据类型的方法。不同点在于,BoundXXXOperations 在创建时,已经指定了 key (bound含义所在),因此在操作的时候,不需要传递 key;同时,所有的 BoundXXXOperations 都继承 BoundKeyOperations ,提供对 key 的操作。
接下来,让我们以 Value 类型为例,分析下 ValueOperations 和 BoundValueOperations的区别。
ValueOperations
BoundValueOperations
void set(K key, V value)
void set(V value)
Boolean setIfAbsent(K key, V value)
Boolean setIfAbsent(V value)
Boolean setIfPresent(K key, V value)
Boolean setIfPresent(V value)
V get(Object key)
V get()
V getAndSet(K key, V value)
V getAndSet(V value)
Long increment(K key)
Long increment()
Long decrement(K key)
Long decrement()
Integer append(K key, String value)
Integer append(String value)
String get(K key, long start, long end)
String get(long start, long end)
void set(K key, V value, long offset)
void set(V value, long offset)
Long size(K key)
Long size()
备注: 重载方法和部分方法省略
从方法对比中,可以非常清楚的验证两者的区别。 除此以外,BoundValueOperations 继承自 BoundKeyOperations,拥有操作 key 的操作。
BoundKeyOperations 核心代码如下:
public interface BoundKeyOperations<K> {
// 获取 key
K getKey();
// 获取 key 类型
DataType getType();
// 获取过期时间
Long getExpire();
// 设置过期时间
Boolean expire(long timeout, TimeUnit unit);
// 设置过期时间
Boolean expireAt(Date date);
// 移除过期时间,使其变为持久化
Boolean persist();
// key 重命名
void rename(K newKey);
}
2.1.3.3 小结
一般情况下,我们以下列操作方式使用 RedisTemplate:
- 创建 RedisTemplate 实例,设置 RedisConnectionFactory 和序列化器,如 KeyRedisSerializer、ValueRedisSerializer、HashKeyRedisSerializer、HashValueRedisSerializer(如果操作 Hash 类型);
- 使用底层方法(execute、executePipeline) 进行 redis 操作;
- 或使用 RedisTemplate 进行 key 相关操作(hasKey、unlink、delete 等);
- 或使用 opsForXXX 获取 XXXOperations,然后通过 XXXOperations 操作对应的数据结构;
- 或使用 boundXXXOps(key) 获取 BoundedXXXOperations,然后通过 BoundedXXXOperations 操作对应数据结构。
2.1.4 StringRedisTemplate
RedisTempalte 存在一个子类 StringRedisTemplate , 该类是 RedisTemplate 在 key 和 value 都是 String 类型的一种泛化。
StringRedisTemplate 核心代码如下:
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
setKeySerializer(());
setValueSerializer(());
setHashKeySerializer(());
setHashValueSerializer(());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
StringRedisTemplate 主要完成:
- 通过继承 RedisTemplate<String, String>,指定 key 和 value 都为 String 类型;
- 将 key、value、 hashKey、hashValue 的序列化器统一设置为 ()。
2.2 Spring Boot 对 RedisTemplate 的支持
Spring Boot 对 RedisTemplate 的支持主要体现在 RedisAutoConfiguration 类中。
RedisAutoConfiguration 核心代码:
@Configuration
// 该配置只有在存在 RedisOperations 类时启用
@ConditionalOnClass()
// 将配置信息绑定到 RedisProperties 类中
@EnableConfigurationProperties()
// 自动引入 LettuceConnectionConfiguration 和 JedisConnectionConfiguration 以完成 JedisConnectionFactory 配置
@Import({ , })
public class RedisAutoConfiguration {
// 如果bean丢失,自动注册类型为 RedisTemplate<Object, Object> 的bean
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
(redisConnectionFactory);
return template;
}
// 如果bean丢失,自动注册类型为 StringRedisTemplate 的bean
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
(redisConnectionFactory);return template;
}
}
转载 : /activity/5c3fe7ef625c8510e5754c91