RedisTemplate 详细解析

118 篇文章 0 订阅
22 篇文章 0 订阅

问题 & 方案

1.1 问题

RedisTemplate 是 Spring Boot 访问 Redis 的核心组件,底层通过 RedisConnectionFactory 对多种 Redis 驱动进行集成,上层通过 XXXOperations 提供丰富的 API ,并结合 Spring4 基于泛型的 bean 注入,极大的提供了便利,成为日常开发的一大利器。

但美中不足的是,需要针对应用场景,使用不同的 RedisSerializer 对RedisTemplate 进行定制,那有没有一种方法能够自动完成 RedisTemplate 的定义呢?

1.2 方案

如果通过注解完成 RedisSerializer 的配置,在 Spring 启动时,对 RedisTemplate 进行自动注册,会是怎样呢?

大概的思路如下:

  1. 自定义一套注解,通过注解完成 RedisTemplate 的 RedisSerializer 配置;
  2. 在 Spring 启动时,对托管 bean 进行功能加强,自动生成 RedisTemplate ;
  3. 自定义 @EnableXXX 注解,完成与 Spring Boot 的集成。

OK,让我们一起开启这次旅行。

基础知识准备

在正式实战前,让我们一起对所需的技术点进行梳理。

所涉及的技术点包括:

  1. RedisTemplate 简介;
  2. Spring Boot 对 RedisTemplate 的支持;
  3. Spring Bean 生命周期;
  4. 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 = LogFactory.getLog(getClass());
    private @Nullable RedisConnectionFactory connectionFactory;
    // 对connectionFactory进行非空检测
    public void afterPropertiesSet() {
        Assert.state(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) {
        this.connectionFactory = connectionFactory;
    }
}

BeanClassLoaderAware 接口为 Spring 的扩展接口,主要完成 ClassLoader 的注入:

// Aware 为标记接口,内部没有任何方法
public interface BeanClassLoaderAware extends Aware {
    void setBeanClassLoader(ClassLoader classLoader);
}

RedisOperations 接口,对 Redis 操作进行封装,是这次分析的重点。

2.1.2 RedisOperations

RedisOperations 接口对 Redis 操作进行封装。

RedisOperations 接口中的方法根据功能大概可以分为以下几类:

  1. execute 模板方法,提供底层 API 操作。通过该方法可以直接访问 RedisConnection 对象;
  2. key 相关方法,直接提供 Redis key 相关操作;
  3. opsForXXX 相关方法,XXXOperations 是 RedisTemplate 对 Redis 数据结构操作的一种高级 API ,这是我们最常使用的接口;
  4. serializer 相关方法,对 RedisTemplate 内部序列化方式进行定制;
  5. 其他方法,包括 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 接口,有几个注意点:

  1. 大量用到泛型类型,这些类型如何进行序列化和反序列化;
  2. 大量的 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:

  1. 创建 RedisTemplate 实例,设置 RedisConnectionFactory 和序列化器,如 KeyRedisSerializer、ValueRedisSerializer、HashKeyRedisSerializer、HashValueRedisSerializer(如果操作 Hash 类型);
  2. 使用底层方法(execute、executePipeline) 进行 redis 操作;
  3. 或使用 RedisTemplate 进行 key 相关操作(hasKey、unlink、delete 等);
  4. 或使用 opsForXXX 获取 XXXOperations,然后通过 XXXOperations 操作对应的数据结构;
  5. 或使用 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(RedisSerializer.string());
        setValueSerializer(RedisSerializer.string());
        setHashKeySerializer(RedisSerializer.string());
        setHashValueSerializer(RedisSerializer.string());
    }
    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        setConnectionFactory(connectionFactory);
        afterPropertiesSet();
    }
    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

StringRedisTemplate 主要完成:

  1. 通过继承 RedisTemplate<String, String>,指定 key 和 value 都为 String 类型;
  2. 将 key、value、 hashKey、hashValue 的序列化器统一设置为 RedisSerializer.string()。

2.2 Spring Boot 对 RedisTemplate 的支持

Spring Boot 对 RedisTemplate 的支持主要体现在 RedisAutoConfiguration 类中。

RedisAutoConfiguration 核心代码:

@Configuration
// 该配置只有在存在 RedisOperations 类时启用
@ConditionalOnClass(RedisOperations.class) 
// 将配置信息绑定到 RedisProperties 类中
@EnableConfigurationProperties(RedisProperties.class)
// 自动引入 LettuceConnectionConfiguration 和 JedisConnectionConfiguration 以完成 JedisConnectionFactory 配置
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
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<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    // 如果bean丢失,自动注册类型为 StringRedisTemplate 的bean
    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);return template;
    }
}

转载 : https://gitchat.csdn.net/activity/5c3fe7ef625c8510e5754c91

  • 1
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值