Spring Data Redis 中的 opsFor 方法深入解析
Spring Data Redis 中的 opsFor 方法詳解
1. opsFor 方法概述
1.1 什么是 opsFor 方法
opsFor 是 RedisTemplate 類中的一系列方法,用于獲取特定數據類型的操作接口。這些方法返回的是 Operations 對象,每個對象都提供了針對特定 Redis 數據類型的操作方法。
// 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 類型操作
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 類型操作
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 類型操作
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 類型操作
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() - 有序集合操作
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 方法
/**
* 其他 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"));
// 創(chuàng)建消費組
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 源碼結構分析
// 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) {
// 創(chuàng)建 DefaultValueOperations 實例
valueOps = new DefaultValueOperations<>(this);
}
}
}
return valueOps;
}
/**
* opsForHash 的實現
* 每次調用都創(chuàng)建新實例(因為泛型參數不同)
*/
@Override
@SuppressWarnings("unchecked")
public <HK, HV> HashOperations<K, HK, HV> opsForHash() {
// 直接創(chuàng)建新實例,不需要緩存
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) {
// 實際執(zhí)行 Redis 命令
execute(new ValueDeserializingRedisCallback(key) {
protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
// 序列化值
byte[] rawValue = serializeValue(value);
// 調用 Redis 連接執(zhí)行 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) {
// 執(zhí)行 GET 命令
return connection.get(rawKey);
}
}, true);
}
// 其他方法的實現...
}
}3.2 執(zhí)行流程解析
執(zhí)行流程:
1. 調用 redisTemplate.opsForValue().set("key", "value")
2. DefaultValueOperations.set() 方法被調用
3. 創(chuàng)建 ValueDeserializingRedisCallback 回調
4. 執(zhí)行 execute() 方法
5. 獲取 RedisConnection(從連接池)
6. 序列化 key 和 value
7. 執(zhí)行 connection.set(rawKey, rawValue)
8. 返回結果(如果有)
9. 釋放連接(歸還到連接池)4. 高級使用技巧
4.1 類型安全操作
/**
* 類型安全的操作示例
*/
@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 批量操作優(yōu)化
/**
* 批量操作優(yōu)化示例
*/
@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());
}
/**
* 批量設置和獲取的優(yōu)化
*/
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
/**
* 自定義 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 使用建議
/**
* 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() {
// 在初始化時獲取一次,后續(xù)直接使用
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);
// 執(zhí)行事務
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(); // 執(zhí)行事務
}
});
}
/**
* 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. 批量操作優(yōu)化
*/
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 常見問題解決方案
/**
* 常見問題和解決方案
*/
@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 所有數據類型的操作
- 良好的抽象:隱藏了底層序列化和連接管理的復雜性
- 事務支持:支持聲明式和編程式事務
- 批量操作優(yōu)化:提供了 Pipeline 和 multi 操作
正確理解和使用 opsFor 方法,可以幫助你更高效、更安全地使用 Redis,構建高性能的應用程序。
到此這篇關于Spring Data Redis 中的 opsFor 方法深入解析的文章就介紹到這了,更多相關Spring Data Redis opsFor 方法內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
舉例講解Java的Spring框架中AOP程序設計方式的使用
這篇文章主要介紹了Java的Spring框架中AOP程序設計方式的使用講解,文中舉的AOP下拋出異常的例子非常實用,需要的朋友可以參考下2016-04-04
使用SpringAop動態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中
這篇文章主要介紹了使用SpringAop動態(tài)獲取mapper執(zhí)行的SQL,并保存SQL到Log表中問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03

