使用的框架
Spring BootSpring Data Redis
RedisTemplates 中封装了很多对象类,我们通过对于类的一个操作,能够实现数据的填充。如
@Override
public ValueOperations<K, V> opsForValue() {
return valueOps;
}
Redis 中的数据是以 key-value 形式的,ValueOperations<K, V> 设计约等于将 key-value 封装成了一个对象。
解析一个基于 AbstractOperations 对象类的序列化与反序列化操作
Redis 会给追加进入的数据进行自动的序列化,在首部进行字符的追加,在存入时会进行序列化,在读取时会进行反序列化。
这个是为什么呢?不是 Redis 的操作,而是我们使用的 Spring Data Redis 进行的操作。在 Java 程序中,Spring Data Redis 提供了丰富的与 Redis 进行操作的接口和封装类。我们使用 Redis 的缓存功能时,我们需要操作提供给我们的接口 RedisTemplate,而 RedisTemplate 中的实现操作类 DefaultValueOperations<K, V>
继承的抽象基类 AbstractOperations 中的源码有非常直观的表现。
以任意一个存取函数为例。这里是取函数 get(),那么我们以 Java 程序的角度为例,存在数据库里的数据库
@Override
public V get(Object key) {
return execute(new ValueDeserializingRedisCallback(key) {
@Override
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
return connection.get(rawKey);
}
}, true);
}
首先,这里做了有且仅有的一个操作,执行 execute(RedisCallBack<T> action, boolean exposeConnection, boolean pipeline) 函数,实例化父类中的内部类 ValueDeserializingRedisCallback,并将 key 值存入其中。也就是说,在 execute() 函数之前,我们已经实例化了具体的操作对象。这是一个解耦设计,使 execute 函数可以专注于逻辑的处理,而并非对对象的控制操作管理。
execute() 函数
位于父类中的 execute 函数的解释如下。总的来说,它是一个执行操作对象的函数。在这里有一个回调设计的布尔开关,这个非常重要,是后续实现对对象中 doInRedis 函数执行的启动开关。
在可公开或不可公开的连接中执行给定的操作对象。此外,该连接可以进行流水线处理。请注意,流水线处理的结果将被丢弃(因此适用于只写场景)
形参:
action – 要执行的回调对象
exposeConnection – 是否强制向回调代码公开原生 Redis 连接
pipeline – 是否将连接进行流水线处理
类型形参:<T> – 返回类型
返回值:操作返回的对象
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it");
Assert.notNull(action, "Callback object must not be null");
RedisConnectionFactory factory = getRequiredConnectionFactory();
RedisConnection conn = RedisConnectionUtils.getConnection(factory, enableTransactionSupport);
try {
boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
RedisConnection connToUse = preProcessConnection(conn, existingConnection);
boolean pipelineStatus = connToUse.isPipelined();
if (pipeline && !pipelineStatus) {
connToUse.openPipeline();
}
RedisConnection connToExpose = (exposeConnection ? connToUse : createRedisConnectionProxy(connToUse));
T result = action.doInRedis(connToExpose);
// close pipeline
if (pipeline && !pipelineStatus) {
connToUse.closePipeline();
}
return postProcessResult(result, connToUse, existingConnection);
} finally {
RedisConnectionUtils.releaseConnection(conn, factory, enableTransactionSupport);
}
}
我们可以看到一个非常整洁自洽的 RedisCallBack<T> 的完整生命周期。在这个 excute() 函数中,我们终于第一次看到了顾名思义的 CallBack(回调)。在整个 exeute() 函数内部,唯一一次调用实例对象的操作(Assert 除外)是 :
T result = action.doInRedis(connToExpose);
这个 doIndedis 函数在我们的情境中,实际上具体的实现如下,可以看到,在这里它最终调用了反序列化函数,并最终返回该结果作为 result。
public final V doInRedis(RedisConnection connection) {
byte[] result = inRedis(rawKey(key), connection);
return deserializeValue(result);
}
execute 函数还负责了 close 连接池的一系列收尾操作。在这里基本完成了一个 get 函数的请求。
内部类 ValueDeserializingRedisCallBack 详细代码
// utility methods for the template internal methods
abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
private Object key;
public ValueDeserializingRedisCallback(Object key) {
this.key = key;
}
public final V doInRedis(RedisConnection connection) {
byte[] result = inRedis(rawKey(key), connection);
return deserializeValue(result);
}
@Nullable
protected abstract byte[] inRedis(byte[] rawKey, RedisConnection connection);
}