sky-take-out項(xiàng)目中Redis的使用示例詳解
Spring Cache 是 Spring Framework 提供的一個(gè)抽象層,用于簡化應(yīng)用程序中的緩存管理。它允許開發(fā)者以聲明式的方式將緩存添加到應(yīng)用中,而不需要手動(dòng)編寫冗長的緩存邏輯代碼。Spring Cache 通過提供一系列注解和配置選項(xiàng),使得集成緩存變得非常簡單。將 Redis 集成到 Spring 應(yīng)用程序中,并將其作為 Spring Cache 的緩存提供者。這樣做的好處是可以在開發(fā)過程中專注于業(yè)務(wù)邏輯的實(shí)現(xiàn),只需通過 Spring Cache 提供的簡單注解即可完成復(fù)雜的緩存管理任務(wù)。與此同時(shí),你還能享受到 Redis 提供的高效性能和豐富的功能特性。這種方法不僅提高了開發(fā)效率,還增強(qiáng)了應(yīng)用的可擴(kuò)展性和性能表現(xiàn)。
Spring Cache主要特性
- 聲明式緩存:通過使用注解(如
@Cacheable,@CachePut,@CacheEvict等),可以非常方便地在方法上定義緩存行為。 - 多種緩存實(shí)現(xiàn)支持:Spring Cache 抽象層支持多種緩存解決方案,包括但不限于 Ehcache, Hazelcast, Infinispan, JCache (JSR-107), Guava Cache, Caffeine, Redis 等。
- 靈活的配置:可以通過 XML 配置、Java Config 或者自動(dòng)配置來設(shè)置緩存管理器(Cache Manager)。
- 條件緩存:可以根據(jù)特定條件決定是否執(zhí)行緩存操作(例如,使用
condition屬性)。 - SpEL 支持:可以在注解中使用 Spring Expression Language (SpEL) 來動(dòng)態(tài)指定緩存鍵或其他屬性值。
核心注解
1.@Cacheable
此注解通常用于查詢操作,當(dāng)調(diào)用被注解的方法時(shí),如果緩存中存在對應(yīng)的數(shù)據(jù),則直接返回緩存結(jié)果,而不是執(zhí)行方法體。
@GetMapping("/list")
@ApiOperation("根據(jù)分類id查詢套餐")
@Cacheable(cacheNames = "setmealCache",key = "#categoryId")
public Result<List<Setmeal>> list(Long categoryId) {
Setmeal setmeal = new Setmeal();
setmeal.setCategoryId(categoryId);
setmeal.setStatus(StatusConstant.ENABLE);
List<Setmeal> list = setmealService.list(setmeal);
return Result.success(list);
}value: 指定緩存的名字。key: 可選參數(shù),用來指定緩存的鍵??梢允褂?SpEL 表達(dá)式。
2.@CachePut
與 @Cacheable 不同,@CachePut 總是會(huì)執(zhí)行方法,并將方法的結(jié)果放入緩存中。適用于更新操作。
@GetMapping("/{id}")
@ApiOperation("根據(jù)id查詢套餐")
@CachePut(cacheNames = "setmealCache",key = "#categoryId")
public Result<SetmealVO> getById(@PathVariable Long id) {
SetmealVO setmealVO = setmealService.getByIdWithDish(id);
return Result.success(setmealVO);
}
3.@CacheEvict
用于清除緩存,適用于刪除或無效化數(shù)據(jù)的操作。
@PostMapping
@ApiOperation("新增套餐")
@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
public Result save(@RequestBody SetmealDTO setmealDTO) {
setmealService.saveWithDish(setmealDTO);
return Result.success();
}
value: 指定緩存的名字。key: 用來指定緩存的鍵??梢允褂?SpEL 表達(dá)式。
@CacheEvict(cacheNames = "setmealCache", allEntries = true)
public Result startOrStop(@PathVariable Integer status, Long id) {
setmealService.startOrStop(status, id);
return Result.success();
}
@CacheEvict(cacheNames = "setmealCache", allEntries = true) 被用來清除名為 setmealCache 的緩存中的所有條目。
cacheNames/value:指定要操作的緩存名稱。在這個(gè)例子中是
"setmealCache"。這兩個(gè)屬性是等價(jià)的,用cacheNames或者value來指定緩存名。allEntries:一個(gè)布爾值,默認(rèn)為
false。如果設(shè)置為true,則表示刪除指定緩存中的所有條目。若為false,則根據(jù)key屬性(如果沒有提供key,則默認(rèn)為空)來決定刪除哪個(gè)具體的緩存項(xiàng)。在例子中,由于allEntries = true,所以無論是否有特定的鍵被定義,都會(huì)清空整個(gè)setmealCache緩存。beforeInvocation:一個(gè)布爾值,默認(rèn)為
false。如果設(shè)置為true,那么緩存的清除會(huì)在方法調(diào)用之前發(fā)生;否則,在方法成功執(zhí)行之后才會(huì)進(jìn)行清除操作。這有助于處理方法執(zhí)行過程中可能發(fā)生的異常情況,避免不必要的緩存清除。使用
allEntries = true會(huì)一次性清除指定緩存中的所有條目,這對于緩存命中率和性能有一定的影響,特別是在緩存非常大的情況下。因此,應(yīng)該謹(jǐn)慎使用,并確保這是必要的操作。
不是所有 Redis 的功能都可以通過 Spring Cache 注解實(shí)現(xiàn)。
?Spring Cache 注解能做什么?
Spring Cache 的設(shè)計(jì)初衷是簡化緩存操作,提供一種統(tǒng)一的、聲明式的緩存管理方式,適用于常見的緩存場景,比如:
- 緩存方法結(jié)果(
@Cacheable) - 更新緩存(
@CachePut) - 刪除緩存(
@CacheEvict) - 組合多個(gè)緩存操作(
@Caching)
這些注解非常適合用于:
- 讀多寫少的業(yè)務(wù)場景(如查詢接口)
- 需要緩存方法返回值的場景
- 避免重復(fù)執(zhí)行耗時(shí)操作(如數(shù)據(jù)庫查詢)
?Spring Cache 注解不能做什么?
雖然 Spring Cache 很方便,但它是一個(gè)抽象層,只封裝了緩存操作中最常見的部分。它并不能覆蓋 Redis 所有的強(qiáng)大功能。以下是一些 Spring Cache 注解無法實(shí)現(xiàn)或難以實(shí)現(xiàn)的功能:
| Redis 功能 | Spring Cache 注解是否支持 |
|---|---|
| 字符串、哈希、列表、集合、有序集合等數(shù)據(jù)結(jié)構(gòu)操作 | ? 不支持,需使用 RedisTemplate 或 StringRedisTemplate |
| 發(fā)布/訂閱消息(Pub/Sub) | ? 不支持 |
| Lua 腳本執(zhí)行 | ? 不支持 |
| 分布式鎖(SETNX / RedLock) | ? 不支持 |
| HyperLogLog、Geo、Bitmap 等高級數(shù)據(jù)結(jié)構(gòu) | ? 不支持 |
| 批量操作(Pipeline) | ? 不支持 |
| 事務(wù)(MULTI / EXEC) | ? 不支持 |
| TTL、KEYS、SCAN 等管理命令 | ? 不支持 |
| 自定義緩存過期策略(比如不同 key 有不同的 TTL) | ?? 部分支持,但不夠靈活 |
???舉個(gè)例子:
場景:緩存用戶信息
@Cacheable("user")
public User getUserById(Long id) {
return userRepository.findById(id);
}
? 適合用 Spring Cache,因?yàn)檫@是個(gè)典型的“查數(shù)據(jù)庫緩存結(jié)果”的場景。
場景:實(shí)現(xiàn)一個(gè)分布式鎖
Boolean success = redisTemplate.opsForValue().setIfAbsent("lock_key", "locked", 30, TimeUnit.SECONDS);
? 無法用 Spring Cache 實(shí)現(xiàn),必須用 RedisTemplate 直接操作 Redis。
?什么時(shí)候用 Spring Cache?
- 你只需要緩存方法的返回值。
- 你希望代碼更簡潔,不想寫大量緩存邏輯。
- 你希望統(tǒng)一管理緩存策略(如過期時(shí)間、緩存名稱)。
- 你未來可能更換緩存實(shí)現(xiàn)(比如從 Redis 換成 Caffeine)。
?什么時(shí)候要直接使用 RedisTemplate?
- 你需要使用 Redis 的高級功能(如 Hash、List、Pub/Sub、Lua、分布式鎖等)。
- 你需要更細(xì)粒度地控制 Redis 操作(如 Pipeline、事務(wù))。
- 你需要實(shí)現(xiàn)一些緩存抽象層無法覆蓋的特殊邏輯。
?最佳實(shí)踐:結(jié)合使用
在實(shí)際項(xiàng)目中,推薦結(jié)合使用 Spring Cache 和 RedisTemplate:
- 對于通用的緩存需求(如方法結(jié)果緩存),使用 Spring Cache 注解。
- 對于需要 Redis 高級特性的場景,使用
RedisTemplate直接操作 Redis。
?? 總結(jié)一句話:
Spring Cache 注解只能覆蓋 Redis 的部分緩存功能,不能替代 Redis 的全部能力。
對于復(fù)雜的 Redis 操作,仍需使用RedisTemplate。
如果正在開發(fā)一個(gè)中大型項(xiàng)目,建議:
- 對通用緩存邏輯使用 Spring Cache 注解;
- 對 Redis 高級功能單獨(dú)封裝成 Redis 工具類或服務(wù)類,使用
RedisTemplate來實(shí)現(xiàn)。
這樣既能享受注解帶來的便捷,又能保留 Redis 的靈活性和強(qiáng)大功能。
1. 字符串、哈希、列表、集合、有序集合等數(shù)據(jù)結(jié)構(gòu)操作
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 主要用于緩存方法的結(jié)果,并不直接支持對 Redis 的各種數(shù)據(jù)類型(如字符串、哈希、列表、集合和有序集合)的操作。
解決方案:
使用 RedisTemplate 或 StringRedisTemplate 來進(jìn)行更復(fù)雜的 Redis 數(shù)據(jù)類型操作。
示例:字符串操作
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void stringOperationsExample(String key, String value) {
// 設(shè)置字符串值
stringRedisTemplate.opsForValue().set(key, value);
// 獲取字符串值
String retrievedValue = stringRedisTemplate.opsForValue().get(key);
System.out.println("Retrieved Value: " + retrievedValue);
}示例:哈希操作
public void hashOperationsExample(String key, String field, String value) {
// 設(shè)置哈希表中的字段值
stringRedisTemplate.opsForHash().put(key, field, value);
// 獲取哈希表中字段的值
Object fieldValue = stringRedisTemplate.opsForHash().get(key, field);
System.out.println("Field Value: " + fieldValue);
}示例:列表操作
public void listOperationsExample(String key, String... values) {
// 右側(cè)插入元素到列表
for (String value : values) {
stringRedisTemplate.opsForList().rightPush(key, value);
}
// 獲取列表中的所有元素
List<String> elements = stringRedisTemplate.opsForList().range(key, 0, -1);
System.out.println("List Elements: " + elements);
}
示例:集合操作
public void setOperationsExample(String key, String... members) {
// 添加成員到集合
for (String member : members) {
stringRedisTemplate.opsForSet().add(key, member);
}
// 獲取集合中的所有成員
Set<String> membersSet = stringRedisTemplate.opsForSet().members(key);
System.out.println("Members of Set: " + membersSet);
}示例:有序集合操作
public void sortedSetOperationsExample(String key, String member, double score) {
// 添加成員到有序集合并指定分?jǐn)?shù)
stringRedisTemplate.opsForZSet().add(key, member, score);
// 獲取有序集合中的所有成員及其分?jǐn)?shù)
Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().rangeWithScores(key, 0, -1);
tuples.forEach(tuple -> System.out.println("Member: " + tuple.getValue() + ", Score: " + tuple.getScore()));
}2. 發(fā)布/訂閱消息(Pub/Sub)
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持發(fā)布/訂閱模式。
解決方案:
使用 RedisTemplate 進(jìn)行發(fā)布/訂閱操作。
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 訂閱者
public void subscribeToChannel(String channelName) {
stringRedisTemplate.getConnectionFactory().getConnection().subscribe((message, pattern) -> {
System.out.println("Received message on channel [" + new String(pattern) + "] with content: " + new String(message.getBody()));
}, channelName.getBytes());
}
// 發(fā)布者
public void publishToChannel(String channelName, String message) {
stringRedisTemplate.convertAndSend(channelName, message);
}3. Lua 腳本執(zhí)行
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持執(zhí)行 Lua 腳本。
解決方案:
使用 RedisTemplate 執(zhí)行 Lua 腳本。
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void executeLuaScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua_script.lua")));
redisScript.setResultType(Long.class);
List<String> keys = Arrays.asList("key1", "key2");
Long result = stringRedisTemplate.execute(redisScript, keys, "arg1");
System.out.println("Lua Script Result: " + result);
}4. 分布式鎖(SETNX / RedLock)
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持分布式鎖機(jī)制。
解決方案:
使用 RedisTemplate 實(shí)現(xiàn)分布式鎖。
@Autowired
private StringRedisTemplate stringRedisTemplate;
public boolean acquireLock(String lockKey, long timeout) throws InterruptedException {
long millisecondsTimeout = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < millisecondsTimeout) {
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "locked", Duration.ofMinutes(1));
if (Boolean.TRUE.equals(success)) {
return true;
}
Thread.sleep(100); // 等待一段時(shí)間后重試
}
return false;
}
public void releaseLock(String lockKey) {
stringRedisTemplate.delete(lockKey);
}5. HyperLogLog、Geo、Bitmap 等高級數(shù)據(jù)結(jié)構(gòu)
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持這些高級數(shù)據(jù)結(jié)構(gòu)。
解決方案:
使用 RedisTemplate 操作這些高級數(shù)據(jù)結(jié)構(gòu)。
Geo 操作示例:
public void geoOperationsExample() {
stringRedisTemplate.opsForGeo().add("cities",
new Point(13.361389, 38.115556), "Palermo",
new Point(15.087269, 37.502669), "Catania");
Distance distance = stringRedisTemplate.opsForGeo().distance("cities", "Palermo", "Catania", RedisGeoCommands.DistanceUnit.KILOMETERS);
System.out.println("Distance between Palermo and Catania: " + distance.getValue() + " km");
}6. 批量操作(Pipeline)
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持批量操作。
解決方案:
使用 RedisTemplate 進(jìn)行批量操作。
public void pipelineOperationsExample() {
List<Object> results = stringRedisTemplate.executePipelined((RedisCallback<String>) connection -> {
connection.openPipeline();
connection.set("key1".getBytes(), "value1".getBytes());
connection.incr("key2".getBytes());
return null;
});
System.out.println("Pipeline Results: " + results);
}7. 事務(wù)(MULTI / EXEC)
Spring Cache 注解的支持情況:
- 不支持。Spring Cache 不支持事務(wù)操作。
解決方案:
使用 RedisTemplate 進(jìn)行事務(wù)操作。
public void transactionOperationsExample() {
stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations) throws DataAccessException {
operations.multi();
operations.opsForValue().increment("key1");
operations.opsForValue().increment("key2");
return operations.exec(); // 執(zhí)行并返回結(jié)果
}
});
}8. TTL、KEYS、SCAN 等管理命令
Spring Cache 注解的支持情況:
- 部分支持。雖然可以設(shè)置全局的 TTL,但不能為每個(gè)鍵單獨(dú)設(shè)置不同的 TTL。
解決方案:
使用 RedisTemplate 設(shè)置 TTL 和執(zhí)行其他管理命令。
public void ttlOperationsExample(String key) {
// 設(shè)置TTL
stringRedisTemplate.expire(key, Duration.ofMinutes(10));
}
public void scanKeysExample() {
ScanOptions options = ScanOptions.scanOptions().match("*").count(100).build();
Cursor<Map.Entry<byte[], byte[]>> cursor = stringRedisTemplate.executeWithStickyConnection(
redisConnection -> new ConvertingCursor<>(redisConnection.scan(options),
entry -> new AbstractMap.SimpleEntry<>(new String(entry.getKey()), new String(entry.getValue()))));
while (cursor.hasNext()) {
Map.Entry<String, String> entry = cursor.next();
System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
}
}通過上述例子可以看出,雖然 Spring Cache 提供了便捷的方式來管理緩存,但對于 Redis 的復(fù)雜功能,需要依賴 RedisTemplate 或 StringRedisTemplate 來實(shí)現(xiàn)。這種方式不僅保留了 Redis 的靈活性,同時(shí)也使得開發(fā)者能夠充分利用 Redis 的強(qiáng)大功能。
到此這篇關(guān)于sky-take-out項(xiàng)目中Redis的使用的文章就介紹到這了,更多相關(guān)sky-take-out redis使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Web導(dǎo)出等比例圖片到Excel的實(shí)現(xiàn)過程
我們使用Java導(dǎo)出圖片到Excel,打開成功導(dǎo)出的Excel一看,商品對應(yīng)的圖片都很規(guī)矩的按照我的設(shè)置鋪滿了整個(gè)單元格,但是,商品圖片卻都變形了,這樣肯定是不行的,于是第一反應(yīng)就是將圖片等比例導(dǎo)出,所以本文本給大家介紹了如何使用Java Web導(dǎo)出等比例圖片到Excel2023-11-11
關(guān)于解決iReport4.1.1無法正常啟動(dòng)或者閃退或者JDK8不兼容的問題
在安裝使用iReport的過程中遇到一個(gè)問題,我的iReport始終不能打開,困擾了我好久。接下來通過本文給大家介紹iReport4.1.1無法正常啟動(dòng)或者閃退或者JDK8不兼容的問題,需要的朋友可以參考下2018-09-09
java聯(lián)調(diào)生成測試數(shù)據(jù)工具類方式
這篇文章主要介紹了java聯(lián)調(diào)生成測試數(shù)據(jù)工具類方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
druid?handleException執(zhí)行流程源碼解析
這篇文章主要為大家介紹了druid?handleException執(zhí)行流程源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
SpringBoot使用Guava實(shí)現(xiàn)日志脫敏的示例代碼
本文主要介紹了SpringBoot使用Guava實(shí)現(xiàn)日志脫敏的示例代碼,使用Guava中的Strings、Maps和CharMatcher類來進(jìn)行日志脫敏,保護(hù)敏感數(shù)據(jù)的安全,感興趣的可以了解一下2024-01-01

