Spring Data Redis 中的 opsFor 方法详解
1. opsFor 方法概述
1.1 什么是 opsFor 方法
opsFor 是 RedisTemplate 类中的一系列方法,用于获取特定数据类型的操作接口。这些方法返回的是 Operations 对象,每个对象都提供了针对特定 Redis 数据类型的操作方法。
java
// opsFor 方法的整体架构
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> {
// 获取 String 类型操作接口
@Override
public ValueOperations<K, V> opsForValue() {
// ...
}
// 获取 Hash 类型操作接口
@Override
public HashOperations<K, HK, HV> opsForHash() {
// ...
}
// 获取 List 类型操作接口
@Override
public ListOperations<K, V> opsForList() {
// ...
}
// 获取 Set 类型操作接口
@Override
public SetOperations<K, V> opsForSet() {
// ...
}
// 获取 ZSet(有序集合)类型操作接口
@Override
public ZSetOperations<K, V> opsForZSet() {
// ...
}
// 获取 Geo(地理位置)操作接口
@Override
public GeoOperations<K, V> opsForGeo() {
// ...
}
// 获取 HyperLogLog 操作接口
@Override
public HyperLogLogOperations<K, V> opsForHyperLogLog() {
// ...
}
// 获取 Stream 操作接口
@Override
public StreamOperations<K, HK, HV> opsForStream() {
// ...
}
// 获取 Cluster 操作接口
@Override
public ClusterOperations<K, V> opsForCluster() {
// ...
}
}
1.2 opsFor 方法的 UML 类图
┌─────────────────────────────────────────────────────────┐
│ RedisTemplate<K, V> │
├─────────────────────────────────────────────────────────┤
│ + opsForValue(): ValueOperations<K, V> │
│ + opsForHash(): HashOperations<K, HK, HV> │
│ + opsForList(): ListOperations<K, V> │
│ + opsForSet(): SetOperations<K, V> │
│ + opsForZSet(): ZSetOperations<K, V> │
│ + opsForGeo(): GeoOperations<K, V> │
│ + opsForHyperLogLog(): HyperLogLogOperations<K, V> │
│ + opsForStream(): StreamOperations<K, HK, HV> │
└─────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ValueOperations │ │HashOperations │ │ListOperations │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│+ set() │ │+ put() │ │+ leftPush() │
│+ get() │ │+ get() │ │+ rightPush() │
│+ increment() │ │+ entries() │ │+ leftPop() │
│+ decrement() │ │+ keys() │ │+ rightPop() │
│+ getAndSet() │ │+ values() │ │+ range() │
│+ multiSet() │ │+ delete() │ │+ size() │
│+ multiGet() │ │+ hasKey() │ │+ index() │
└─────────────────┘ └─────────────────┘ └─────────────────┘
2. 各个 opsFor 方法详解
2.1 opsForValue() - String 类型操作
java
import org.springframework.data.redis.core.ValueOperations;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* ValueOperations 接口详解
* 对应 Redis 的 String 数据类型
*/
public class ValueOperationsExample {
private ValueOperations<String, Object> valueOps;
public ValueOperationsExample(RedisTemplate<String, Object> redisTemplate) {
// 获取 ValueOperations 实例
this.valueOps = redisTemplate.opsForValue();
}
/**
* 核心方法示例
*/
public void demonstrateMethods() {
// 1. 基本设置和获取
valueOps.set("key1", "value1"); // 设置值
Object value = valueOps.get("key1"); // 获取值
System.out.println("获取的值: " + value);
// 2. 设置值并指定过期时间
valueOps.set("key2", "value2", 60, TimeUnit.SECONDS); // 60秒后过期
// 3. 如果键不存在则设置
Boolean setIfAbsent = valueOps.setIfAbsent("key3", "value3");
System.out.println("是否设置成功(仅当key不存在): " + setIfAbsent);
// 4. 如果键存在则设置
Boolean setIfPresent = valueOps.setIfPresent("key1", "newValue");
System.out.println("是否设置成功(仅当key存在): " + setIfPresent);
// 5. 原子递增和递减
Long incremented = valueOps.increment("counter", 1L); // 递增1
Long decremented = valueOps.decrement("counter", 1L); // 递减1
// 6. 获取并设置(原子操作)
Object oldValue = valueOps.getAndSet("key1", "newValue1");
System.out.println("旧值: " + oldValue);
// 7. 批量操作
Map<String, Object> map = Map.of("key4", "value4", "key5", "value5");
valueOps.multiSet(map); // 批量设置
// 8. 批量获取
Object value4 = valueOps.get("key4");
Object value5 = valueOps.get("key5");
// 9. 获取字符串的一部分(GETRANGE)
String partial = (String) valueOps.get("key1", 0, 3); // 获取索引0-3的字符
// 10. 获取值的长度
Long size = valueOps.size("key1");
System.out.println("值的长度: " + size);
}
/**
* ValueOperations 的完整方法列表
*/
public void showAllMethods() {
// 设置操作
valueOps.set("key", "value");
valueOps.set("key", "value", 10, TimeUnit.SECONDS);
valueOps.setIfAbsent("key", "value");
valueOps.setIfPresent("key", "value");
valueOps.multiSet(Map.of("k1", "v1", "k2", "v2"));
// 获取操作
Object value = valueOps.get("key");
Object andSet = valueOps.getAndSet("key", "newValue");
Map<String, Object> multiGet = valueOps.multiGet(Arrays.asList("k1", "k2"));
// 数值操作
Long increment = valueOps.increment("counter");
Long incrementBy = valueOps.increment("counter", 5L);
Long decrement = valueOps.decrement("counter");
Long decrementBy = valueOps.decrement("counter", 5L);
Double incrementDouble = valueOps.increment("doubleCounter", 1.5);
// 其他操作
Long size = valueOps.size("key");
Boolean append = valueOps.append("key", "suffix"); // 追加字符串
String getRange = (String) valueOps.get("key", 0, 4); // 获取子串
valueOps.set("key", "value", 0, 4); // 设置子串
}
}
2.2 opsForHash() - Hash 类型操作
java
import org.springframework.data.redis.core.HashOperations;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* HashOperations 接口详解
* 对应 Redis 的 Hash 数据类型
*/
public class HashOperationsExample {
private HashOperations<String, String, Object> hashOps;
public HashOperationsExample(RedisTemplate<String, Object> redisTemplate) {
// 获取 HashOperations 实例
this.hashOps = redisTemplate.opsForHash();
}
/**
* Hash 操作的核心方法
*/
public void demonstrateMethods() {
String hashKey = "user:1001";
// 1. 设置单个字段
hashOps.put(hashKey, "name", "张三");
hashOps.put(hashKey, "age", 25);
hashOps.put(hashKey, "email", "zhangsan@example.com");
// 2. 获取单个字段
Object name = hashOps.get(hashKey, "name");
System.out.println("姓名: " + name);
// 3. 批量设置多个字段
Map<String, Object> userData = Map.of(
"address", "北京市",
"phone", "13800138000",
"gender", "男"
);
hashOps.putAll(hashKey, userData);
// 4. 获取所有字段和值
Map<String, Object> allData = hashOps.entries(hashKey);
System.out.println("所有数据: " + allData);
// 5. 获取所有字段名
Set<String> keys = hashOps.keys(hashKey);
System.out.println("所有字段: " + keys);
// 6. 获取所有字段值
List<Object> values = hashOps.values(hashKey);
System.out.println("所有值: " + values);
// 7. 删除字段
Long deletedCount = hashOps.delete(hashKey, "phone", "gender");
System.out.println("删除的字段数量: " + deletedCount);
// 8. 判断字段是否存在
Boolean hasName = hashOps.hasKey(hashKey, "name");
System.out.println("是否存在name字段: " + hasName);
// 9. 字段值的递增(原子操作)
Long newAge = hashOps.increment(hashKey, "age", 1L);
System.out.println("递增后的年龄: " + newAge);
// 10. 获取字段数量
Long size = hashOps.size(hashKey);
System.out.println("字段数量: " + size);
// 11. 扫描(用于大数据集)
Cursor<Map.Entry<String, Object>> cursor = hashOps.scan(
hashKey,
ScanOptions.scanOptions()
.match("n*") // 匹配以n开头的字段
.count(10) // 每次扫描10条
.build()
);
// 12. 获取多个字段的值
List<Object> multiValues = hashOps.multiGet(hashKey, Arrays.asList("name", "age", "email"));
System.out.println("多个字段的值: " + multiValues);
}
}
2.3 opsForList() - List 类型操作
java
import org.springframework.data.redis.core.ListOperations;
import java.util.List;
/**
* ListOperations 接口详解
* 对应 Redis 的 List 数据类型
*/
public class ListOperationsExample {
private ListOperations<String, Object> listOps;
public ListOperationsExample(RedisTemplate<String, Object> redisTemplate) {
// 获取 ListOperations 实例
this.listOps = redisTemplate.opsForList();
}
/**
* List 操作的核心方法
*/
public void demonstrateMethods() {
String listKey = "tasks";
// 1. 从左侧插入元素
Long leftPush = listOps.leftPush(listKey, "task1");
Long leftPushAll = listOps.leftPushAll(listKey, "task2", "task3", "task4");
// 2. 从右侧插入元素
Long rightPush = listOps.rightPush(listKey, "task5");
Long rightPushAll = listOps.rightPushAll(listKey, "task6", "task7");
// 3. 从左侧弹出元素
Object leftPop = listOps.leftPop(listKey);
System.out.println("左侧弹出的元素: " + leftPop);
// 4. 从右侧弹出元素
Object rightPop = listOps.rightPop(listKey);
System.out.println("右侧弹出的元素: " + rightPop);
// 5. 阻塞式弹出(等待指定时间)
Object leftPopWithTimeout = listOps.leftPop(listKey, 10, TimeUnit.SECONDS);
Object rightPopWithTimeout = listOps.rightPop(listKey, 10, TimeUnit.SECONDS);
// 6. 获取列表指定范围的元素
List<Object> range = listOps.range(listKey, 0, -1); // 获取所有元素
System.out.println("所有任务: " + range);
// 7. 获取列表长度
Long size = listOps.size(listKey);
System.out.println("列表长度: " + size);
// 8. 根据索引获取元素
Object indexElement = listOps.index(listKey, 1); // 获取索引为1的元素
System.out.println("索引1的元素: " + indexElement);
// 9. 在指定元素前后插入
Long insertAfter = listOps.rightPush(listKey, "task3", "task3.5");
Long insertBefore = listOps.leftPush(listKey, "task3", "task2.5");
// 10. 设置指定索引的值
listOps.set(listKey, 0, "newTask1");
// 11. 删除元素
// 删除前2个值为"task3"的元素
Long removed = listOps.remove(listKey, 2, "task3");
System.out.println("删除的元素数量: " + removed);
// 12. 裁剪列表(保留指定范围)
listOps.trim(listKey, 0, 4); // 只保留前5个元素
// 13. 移动元素(原子操作)
// 从sourceKey的右侧弹出,推入destKey的左侧
Object moved = listOps.rightPopAndLeftPush("sourceList", "destList");
// 14. 阻塞式移动元素
Object movedWithTimeout = listOps.rightPopAndLeftPush(
"sourceList",
"destList",
10,
TimeUnit.SECONDS
);
}
}
2.4 opsForSet() - Set 类型操作
java
import org.springframework.data.redis.core.SetOperations;
import java.util.Set;
/**
* SetOperations 接口详解
* 对应 Redis 的 Set 数据类型
*/
public class SetOperationsExample {
private SetOperations<String, Object> setOps;
public SetOperationsExample(RedisTemplate<String, Object> redisTemplate) {
// 获取 SetOperations 实例
this.setOps = redisTemplate.opsForSet();
}
/**
* Set 操作的核心方法
*/
public void demonstrateMethods() {
String setKey = "tags";
String otherSetKey = "categories";
// 1. 添加元素
Long added = setOps.add(setKey, "java", "spring", "redis", "database");
System.out.println("添加的元素数量: " + added);
// 2. 移除元素
Long removed = setOps.remove(setKey, "database");
System.out.println("移除的元素数量: " + removed);
// 3. 获取所有元素
Set<Object> members = setOps.members(setKey);
System.out.println("所有元素: " + members);
// 4. 随机获取元素
Object random = setOps.randomMember(setKey);
System.out.println("随机元素: " + random);
// 5. 随机获取多个元素(可能有重复)
List<Object> randomMembers = setOps.randomMembers(setKey, 2);
// 6. 随机获取多个元素(不重复)
Set<Object> distinctRandomMembers = setOps.distinctRandomMembers(setKey, 2);
// 7. 判断元素是否存在
Boolean isMember = setOps.isMember(setKey, "spring");
System.out.println("是否存在spring: " + isMember);
// 8. 移动元素到另一个集合
Boolean moved = setOps.move(setKey, "java", otherSetKey);
System.out.println("是否移动成功: " + moved);
// 9. 获取集合大小
Long size = setOps.size(setKey);
System.out.println("集合大小: " + size);
// 10. 集合运算
// 交集
Set<Object> intersect = setOps.intersect(setKey, otherSetKey);
// 并集
Set<Object> union = setOps.union(setKey, otherSetKey);
// 差集
Set<Object> difference = setOps.difference(setKey, otherSetKey);
// 11. 集合运算并存储结果
Long intersectAndStore = setOps.intersectAndStore(setKey, otherSetKey, "result:intersect");
Long unionAndStore = setOps.unionAndStore(setKey, otherSetKey, "result:union");
Long differenceAndStore = setOps.differenceAndStore(setKey, otherSetKey, "result:difference");
// 12. 扫描(用于大数据集)
Cursor<Object> cursor = setOps.scan(
setKey,
ScanOptions.scanOptions()
.match("*sp*") // 匹配包含sp的元素
.count(10)
.build()
);
// 13. 弹出随机元素(移除并返回)
Object popped = setOps.pop(setKey);
System.out.println("弹出的元素: " + popped);
// 14. 弹出多个随机元素
List<Object> poppedMultiple = setOps.pop(setKey, 2);
// 15. 多个集合的并集
Set<Object> multiUnion = setOps.union(setKey, otherSetKey, "thirdSetKey");
}
}
2.5 opsForZSet() - 有序集合操作
java
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
* ZSetOperations 接口详解
* 对应 Redis 的 Sorted Set 数据类型
*/
public class ZSetOperationsExample {
private ZSetOperations<String, Object> zSetOps;
public ZSetOperationsExample(RedisTemplate<String, Object> redisTemplate) {
// 获取 ZSetOperations 实例
this.zSetOps = redisTemplate.opsForZSet();
}
/**
* ZSet 操作的核心方法
*/
public void demonstrateMethods() {
String zSetKey = "leaderboard";
// 1. 添加元素(带分数)
Boolean added = zSetOps.add(zSetKey, "player1", 100.0);
System.out.println("是否添加成功: " + added);
// 2. 批量添加元素
Set<TypedTuple<Object>> tuples = new HashSet<>();
tuples.add(new DefaultTypedTuple<>("player2", 150.0));
tuples.add(new DefaultTypedTuple<>("player3", 80.0));
tuples.add(new DefaultTypedTuple<>("player4", 200.0));
Long addedCount = zSetOps.add(zSetKey, tuples);
System.out.println("批量添加的数量: " + addedCount);
// 3. 获取元素的分数
Double score = zSetOps.score(zSetKey, "player1");
System.out.println("player1的分数: " + score);
// 4. 递增分数
Double newScore = zSetOps.incrementScore(zSetKey, "player1", 50.0);
System.out.println("递增后的分数: " + newScore);
// 5. 按分数范围获取元素(从小到大)
Set<Object> range = zSetOps.range(zSetKey, 0, 2); // 获取前3名
System.out.println("前3名: " + range);
// 6. 按分数范围获取元素(从大到小)
Set<Object> reverseRange = zSetOps.reverseRange(zSetKey, 0, 2);
System.out.println("前3名(降序): " + reverseRange);
// 7. 按分数范围获取元素(带分数)
Set<TypedTuple<Object>> rangeWithScores = zSetOps.rangeWithScores(zSetKey, 0, -1);
for (TypedTuple<Object> tuple : rangeWithScores) {
System.out.println(tuple.getValue() + ": " + tuple.getScore());
}
// 8. 按排名范围获取元素(带分数,降序)
Set<TypedTuple<Object>> reverseRangeWithScores = zSetOps.reverseRangeWithScores(zSetKey, 0, 2);
// 9. 获取元素的排名(从小到大,0开始)
Long rank = zSetOps.rank(zSetKey, "player1");
System.out.println("player1的排名: " + rank);
// 10. 获取元素的排名(从大到小)
Long reverseRank = zSetOps.reverseRank(zSetKey, "player1");
System.out.println("player1的排名(降序): " + reverseRank);
// 11. 获取分数在指定范围内的元素数量
Long count = zSetOps.count(zSetKey, 100.0, 200.0);
System.out.println("分数在100-200之间的数量: " + count);
// 12. 获取集合大小
Long size = zSetOps.size(zSetKey);
System.out.println("有序集合大小: " + size);
// 13. 移除元素
Long removed = zSetOps.remove(zSetKey, "player3");
System.out.println("移除的元素数量: " + removed);
// 14. 按排名范围移除元素
Long removedByRank = zSetOps.removeRange(zSetKey, 0, 0); // 移除最后一名
System.out.println("按排名移除的数量: " + removedByRank);
// 15. 按分数范围移除元素
Long removedByScore = zSetOps.removeRangeByScore(zSetKey, 0.0, 100.0);
System.out.println("按分数移除的数量: " + removedByScore);
// 16. 交集运算
Long intersectAndStore = zSetOps.intersectAndStore(zSetKey, "otherZSet", "result:intersect");
// 17. 并集运算
Long unionAndStore = zSetOps.unionAndStore(zSetKey, "otherZSet", "result:union");
// 18. 扫描(用于大数据集)
Cursor<TypedTuple<Object>> cursor = zSetOps.scan(
zSetKey,
ScanOptions.scanOptions()
.match("player*")
.count(10)
.build()
);
// 19. 获取指定分数范围内的元素(按分数从小到大)
Set<Object> rangeByScore = zSetOps.rangeByScore(zSetKey, 100.0, 200.0);
// 20. 获取指定分数范围内的元素(带偏移和限制)
Set<Object> rangeByScoreWithLimit = zSetOps.rangeByScore(
zSetKey,
100.0,
200.0,
0L, // 偏移量
10L // 限制数量
);
}
}
2.6 其他 opsFor 方法
java
/**
* 其他 opsFor 方法的使用示例
*/
public class OtherOperationsExample {
private RedisTemplate<String, Object> redisTemplate;
public OtherOperationsExample(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* GeoOperations - 地理位置操作
*/
public void demonstrateGeoOperations() {
GeoOperations<String, Object> geoOps = redisTemplate.opsForGeo();
String geoKey = "cities";
// 添加地理位置
Point point = new Point(116.397128, 39.916527); // 北京
geoOps.add(geoKey, point, "Beijing");
// 获取地理位置
List<Point> position = geoOps.position(geoKey, "Beijing");
// 计算距离
Distance distance = geoOps.distance(geoKey, "Beijing", "Shanghai");
// 根据位置半径查询
Circle circle = new Circle(point, new Distance(100, Metrics.KILOMETERS));
GeoResults<RedisGeoCommands.GeoLocation<Object>> radius =
geoOps.radius(geoKey, circle);
// 根据成员半径查询
GeoResults<RedisGeoCommands.GeoLocation<Object>> radiusByMember =
geoOps.radius(geoKey, "Beijing", new Distance(200, Metrics.KILOMETERS));
}
/**
* HyperLogLogOperations - 基数统计
*/
public void demonstrateHyperLogLogOperations() {
HyperLogLogOperations<String, Object> hllOps = redisTemplate.opsForHyperLogLog();
// 添加元素
Long added = hllOps.add("hll:uv", "user1", "user2", "user3");
// 统计基数(估算不重复元素数量)
Long size = hllOps.size("hll:uv");
System.out.println("UV估算值: " + size);
// 合并多个HyperLogLog
Long unionSize = hllOps.union("hll:result", "hll:uv1", "hll:uv2");
}
/**
* StreamOperations - 流操作
*/
public void demonstrateStreamOperations() {
StreamOperations<String, String, Object> streamOps = redisTemplate.opsForStream();
// 添加消息到流
MapRecord<String, String, Object> record =
StreamRecords.newRecord()
.in("mystream")
.ofMap(Map.of("field1", "value1", "field2", "value2"))
.withId(RecordId.autoGenerate());
RecordId recordId = streamOps.add(record);
// 读取消息
List<MapRecord<String, String, Object>> records =
streamOps.read(StreamOffset.fromStart("mystream"));
// 创建消费组
streamOps.createGroup("mystream", ReadOffset.from("0"), "mygroup");
// 消费消息
List<MapRecord<String, String, Object>> consumed =
streamOps.read(
Consumer.from("mygroup", "consumer1"),
StreamOffset.create("mystream", ReadOffset.lastConsumed())
);
}
}
3. opsFor 方法的源码解析
3.1 源码结构分析
java
// RedisTemplate 中 opsFor 方法的源码实现
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> {
// 缓存各种 Operations 实例
private volatile ValueOperations<K, V> valueOps;
private volatile ListOperations<K, V> listOps;
private volatile SetOperations<K, V> setOps;
private volatile ZSetOperations<K, V> zSetOps;
private volatile GeoOperations<K, V> geoOps;
private volatile HyperLogLogOperations<K, V> hllOps;
private volatile ClusterOperations<K, V> clusterOps;
private volatile StreamOperations<K, ?, ?> streamOps;
/**
* opsForValue 的实现
* 采用双重检查锁的单例模式
*/
@Override
public ValueOperations<K, V> opsForValue() {
if (valueOps == null) {
synchronized (this) {
if (valueOps == null) {
// 创建 DefaultValueOperations 实例
valueOps = new DefaultValueOperations<>(this);
}
}
}
return valueOps;
}
/**
* opsForHash 的实现
* 每次调用都创建新实例(因为泛型参数不同)
*/
@Override
@SuppressWarnings("unchecked")
public <HK, HV> HashOperations<K, HK, HV> opsForHash() {
// 直接创建新实例,不需要缓存
return new DefaultHashOperations<>(this);
}
/**
* opsForList 的实现
*/
@Override
public ListOperations<K, V> opsForList() {
if (listOps == null) {
synchronized (this) {
if (listOps == null) {
listOps = new DefaultListOperations<>(this);
}
}
}
return listOps;
}
// 其他 opsFor 方法类似...
/**
* 内部类:DefaultValueOperations 的实现
*/
private class DefaultValueOperations extends AbstractOperations<K, V> implements ValueOperations<K, V> {
DefaultValueOperations(RedisTemplate<K, V> template) {
super(template);
}
@Override
public void set(K key, V value) {
// 实际执行 Redis 命令
execute(new ValueDeserializingRedisCallback(key) {
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
// 序列化值
byte[] rawValue = serializeValue(value);
// 调用 Redis 连接执行 SET 命令
connection.set(rawKey, rawValue);
return null;
}
}, true);
}
@Override
public V get(Object key) {
return execute(new ValueDeserializingRedisCallback(key) {
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
// 执行 GET 命令
return connection.get(rawKey);
}
}, true);
}
// 其他方法的实现...
}
}
3.2 执行流程解析
执行流程:
1. 调用 redisTemplate.opsForValue().set("key", "value")
2. DefaultValueOperations.set() 方法被调用
3. 创建 ValueDeserializingRedisCallback 回调
4. 执行 execute() 方法
5. 获取 RedisConnection(从连接池)
6. 序列化 key 和 value
7. 执行 connection.set(rawKey, rawValue)
8. 返回结果(如果有)
9. 释放连接(归还到连接池)
4. 高级使用技巧
4.1 类型安全操作
java
/**
* 类型安全的操作示例
*/
@Component
public class TypeSafeOperations {
private final RedisTemplate<String, User> userRedisTemplate;
private final RedisTemplate<String, Product> productRedisTemplate;
public TypeSafeOperations(
@Qualifier("userRedisTemplate") RedisTemplate<String, User> userRedisTemplate,
@Qualifier("productRedisTemplate") RedisTemplate<String, Product> productRedisTemplate) {
this.userRedisTemplate = userRedisTemplate;
this.productRedisTemplate = productRedisTemplate;
}
/**
* 用户相关操作
*/
public void userOperations() {
ValueOperations<String, User> userValueOps = userRedisTemplate.opsForValue();
HashOperations<String, String, User> userHashOps = userRedisTemplate.opsForHash();
// 类型安全的设置和获取
User user = new User("1001", "张三", 25);
userValueOps.set("user:1001", user);
User retrievedUser = userValueOps.get("user:1001");
// retrievedUser 已经是 User 类型,不需要强制转换
System.out.println("用户年龄: " + retrievedUser.getAge());
}
/**
* 商品相关操作
*/
public void productOperations() {
ValueOperations<String, Product> productValueOps = productRedisTemplate.opsForValue();
ZSetOperations<String, Product> productZSetOps = productRedisTemplate.opsForZSet();
Product product = new Product("P001", "iPhone", 6999.99);
productValueOps.set("product:P001", product);
// 有序集合操作
productZSetOps.add("product:ranking", product, 100.0);
// 获取时自动反序列化为 Product 类型
Set<Product> topProducts = productZSetOps.range("product:ranking", 0, 9);
}
}
/**
* 配置多个 RedisTemplate
*/
@Configuration
public class MultipleRedisTemplateConfig {
@Bean
public RedisTemplate<String, User> userRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, User> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));
return template;
}
@Bean
public RedisTemplate<String, Product> productRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Product> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new Jackson2JsonRedisSerializer<>(Product.class));
return template;
}
}
4.2 批量操作优化
java
/**
* 批量操作优化示例
*/
@Component
public class BatchOperations {
private final RedisTemplate<String, Object> redisTemplate;
public BatchOperations(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
/**
* 使用 Pipeline 批量操作
*/
public void pipelineExample() {
List<Object> results = redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException {
// 开启管道
connection.openPipeline();
// 批量设置值
for (int i = 0; i < 1000; i++) {
byte[] key = ("key:" + i).getBytes();
byte[] value = ("value:" + i).getBytes();
connection.set(key, value);
}
// 批量获取值
for (int i = 0; i < 1000; i++) {
byte[] key = ("key:" + i).getBytes();
connection.get(key);
}
// 管道会自动提交
return null;
}
});
// results 包含所有命令的返回结果
System.out.println("管道操作结果数量: " + results.size());
}
/**
* 批量设置和获取的优化
*/
public void multiOperations() {
ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();
// 批量设置(一次网络请求)
Map<String, Object> batchData = new HashMap<>();
for (int i = 0; i < 100; i++) {
batchData.put("batch:key:" + i, "value:" + i);
}
valueOps.multiSet(batchData);
// 批量获取(一次网络请求)
List<String> keys = new ArrayList<>();
for (int i = 0; i < 100; i++) {
keys.add("batch:key:" + i);
}
List<Object> values = valueOps.multiGet(keys);
}
}
4.3 自定义 Operations
java
/**
* 自定义 Operations 扩展
*/
public class CustomRedisTemplate<K, V> extends RedisTemplate<K, V> {
/**
* 自定义操作:带重试机制的设置操作
*/
public interface CustomValueOperations<K, V> extends ValueOperations<K, V> {
/**
* 带重试的 set 操作
*/
boolean setWithRetry(K key, V value, int maxRetries, long retryInterval);
/**
* 带条件判断的设置
*/
boolean setIf(K key, V value, Predicate<V> condition);
}
/**
* 提供自定义操作接口
*/
public CustomValueOperations<K, V> opsForCustomValue() {
return new DefaultCustomValueOperations<>(this);
}
/**
* 实现类
*/
private class DefaultCustomValueOperations<K, V> extends DefaultValueOperations<K, V>
implements CustomValueOperations<K, V> {
DefaultCustomValueOperations(RedisTemplate<K, V> template) {
super(template);
}
@Override
public boolean setWithRetry(K key, V value, int maxRetries, long retryInterval) {
int retryCount = 0;
while (retryCount < maxRetries) {
try {
set(key, value);
return true;
} catch (Exception e) {
retryCount++;
if (retryCount >= maxRetries) {
throw e;
}
try {
Thread.sleep(retryInterval);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
}
}
return false;
}
@Override
public boolean setIf(K key, V value, Predicate<V> condition) {
V currentValue = get(key);
if (condition.test(currentValue)) {
set(key, value);
return true;
}
return false;
}
}
}
5. 最佳实践和注意事项
5.1 使用建议
java
/**
* opsFor 方法的最佳实践
*/
@Component
public class BestPractices {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 1. 缓存 Operations 引用,避免重复获取
*/
private ValueOperations<String, Object> valueOps;
private HashOperations<String, String, Object> hashOps;
@PostConstruct
public void init() {
// 在初始化时获取一次,后续直接使用
valueOps = redisTemplate.opsForValue();
hashOps = redisTemplate.opsForHash();
}
/**
* 2. 使用合适的序列化器
*/
public void useProperSerializer() {
// 字符串使用 StringRedisSerializer
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
ValueOperations<String, String> stringOps = stringRedisTemplate.opsForValue();
// 对象使用 Jackson2JsonRedisSerializer
RedisTemplate<String, User> objectTemplate = new RedisTemplate<>();
objectTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));
}
/**
* 3. 合理使用事务
*/
public void useTransaction() {
// 开启事务支持
redisTemplate.setEnableTransactionSupport(true);
// 执行事务
redisTemplate.execute(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.multi(); // 开始事务
operations.opsForValue().set("key1", "value1");
operations.opsForHash().put("hash1", "field1", "value1");
return operations.exec(); // 执行事务
}
});
}
/**
* 4. 处理空值
*/
public void handleNullValues() {
// 避免缓存null值
String key = "nonExistentKey";
Object value = valueOps.get(key);
if (value == null) {
// 从数据库查询
value = queryFromDatabase(key);
if (value != null) {
valueOps.set(key, value);
} else {
// 缓存空值标记,防止缓存穿透
valueOps.set(key, "NULL", 60, TimeUnit.SECONDS);
}
} else if ("NULL".equals(value)) {
// 处理空值标记
value = null;
}
}
/**
* 5. 批量操作优化
*/
public void batchOptimization() {
// 使用 multiGet 代替多次 get
List<String> keys = Arrays.asList("key1", "key2", "key3");
List<Object> values = valueOps.multiGet(keys);
// 使用 multiSet 代替多次 set
Map<String, Object> data = new HashMap<>();
data.put("key1", "value1");
data.put("key2", "value2");
valueOps.multiSet(data);
}
private Object queryFromDatabase(String key) {
// 模拟数据库查询
return null;
}
}
5.2 常见问题解决方案
java
/**
* 常见问题和解决方案
*/
@Component
public class CommonIssues {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 问题1:序列化不一致导致读取失败
*/
public void serializationIssue() {
// 错误:使用不同的序列化器写入和读取
RedisTemplate<String, Object> template1 = new RedisTemplate<>();
template1.setValueSerializer(new JdkSerializationRedisSerializer());
RedisTemplate<String, Object> template2 = new RedisTemplate<>();
template2.setValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
// 解决方案:统一序列化器
}
/**
* 问题2:连接泄漏
*/
public void connectionLeak() {
// 错误:没有正确关闭连接
// ListOperations<String, Object> listOps = redisTemplate.opsForList();
// 长时间持有 Operations 对象
// 解决方案:Operations 是线程安全的,可以长期持有
// 但不要持有 RedisConnection
}
/**
* 问题3:大 Key 问题
*/
public void bigKeyIssue() {
// 错误:存储过大的值
// valueOps.set("bigKey", hugeData);
// 解决方案:分片存储
String bigKey = "bigData";
int chunkSize = 1024 * 1024; // 1MB
// 分割数据并存储
List<byte[]> chunks = splitData(hugeData, chunkSize);
for (int i = 0; i < chunks.size(); i++) {
hashOps.put(bigKey, "chunk_" + i, chunks.get(i));
}
}
/**
* 问题4:Hot Key 问题
*/
public void hotKeyIssue() {
// 错误:某个 Key 访问过于频繁
// 频繁调用 valueOps.get("hotKey")
// 解决方案:本地缓存 + 随机过期时间
// 1. 使用本地缓存(如 Caffeine)
// 2. 设置随机过期时间,避免缓存雪崩
valueOps.set("key", "value",
60 + new Random().nextInt(30), // 60-90秒随机过期
TimeUnit.SECONDS
);
}
private List<byte[]> splitData(byte[] data, int chunkSize) {
// 分割数据
List<byte[]> chunks = new ArrayList<>();
for (int i = 0; i < data.length; i += chunkSize) {
int end = Math.min(data.length, i + chunkSize);
chunks.add(Arrays.copyOfRange(data, i, end));
}
return chunks;
}
}
总结
opsFor 方法是 Spring Data Redis 的核心特性,它提供了:
- 类型安全的操作接口:每个数据类型都有专用的操作接口
- 丰富的操作方法:覆盖了 Redis 所有数据类型的操作
- 良好的抽象:隐藏了底层序列化和连接管理的复杂性
- 事务支持:支持声明式和编程式事务
- 批量操作优化:提供了 Pipeline 和 multi 操作
正确理解和使用 opsFor 方法,可以帮助你更高效、更安全地使用 Redis,构建高性能的应用程序。