SpringBoot緩存注解之@Cacheable/@CacheEvict使用方式
在 SpringBoot 里做緩存,除了手動操作 RedisTem)plate,更優(yōu)雅、更常用的方式就是Spring 自帶的緩存注解。
不用寫重復(fù)的緩存邏輯,只需要在方法上加個注解,就能自動實現(xiàn)緩存讀寫,代碼干凈又好維護。
本篇文章就來講講最核心的兩個注解:
- @Cacheable:查詢時自動緩存
- @CacheEvict:更新/刪除時自動清理緩存
一、為什么要用緩存注解?
- 無侵入:業(yè)務(wù)代碼和緩存代碼分離,不污染邏輯
- 極簡開發(fā):一行注解替代一堆
get/set緩存代碼 - 統(tǒng)一管理:過期時間、緩存名稱、key 規(guī)則集中配置
- 適配多種緩存:Redis、Caffeine、內(nèi)存緩存都支持
適合:
- 查詢多、修改少的接口
- 商品詳情、用戶信息、字典數(shù)據(jù)、配置列表
- 不想寫重復(fù)緩存模板代碼的場景
二、基礎(chǔ)環(huán)境準備
1. 引入依賴
<!-- Redis + 緩存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>2. 啟動類開啟緩存
@SpringBootApplication
@EnableCaching // 必須加
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}3. application.yml 緩存配置
spring:
cache:
type: redis
redis:
time-to-live: 3600000 # 默認1小時過期
cache-null-values: false # 不緩存null三、核心注解 1:@Cacheable(查詢 + 緩存)
作用:
第一次執(zhí)行方法 → 查庫 → 結(jié)果存入緩存
后續(xù)請求 → 直接走緩存,不執(zhí)行方法體
1. 基礎(chǔ)用法
@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
return userMapper.selectById(userId);
}-
value / cacheNames:緩存名(區(qū)分不同業(yè)務(wù)) -
key:緩存 key,支持 SpEL 表達式
最終 Redis key:
userCache::1001
2. 常用 key 寫法
// 單個參數(shù) @Cacheable(value = "user", key = "#id") // 對象參數(shù)取id @Cacheable(value = "user", key = "#user.id") // 方法名當key @Cacheable(value = "dict", key = "#root.methodName") // 組合key @Cacheable(value = "order", key = "'uid:'+#userId+':type:'+#type")
3. 條件緩存(滿足才緩存)
// 只緩存成年用戶 @Cacheable(value = "user", key = "#userId", condition = "#result.age >= 18") // 結(jié)果不為null才緩存 @Cacheable(value = "user", key = "#userId", unless = "#result == null")
四、核心注解 2:@CacheEvict(刪除緩存)
作用: 數(shù)據(jù)更新/刪除后,清理舊緩存,保證數(shù)據(jù)一致。
1. 根據(jù) key 刪除
@CacheEvict(value = "userCache", key = "#user.id")
public void updateUser(User user) {
userMapper.updateById(user);
}2. 刪除整個緩存名下所有 key
@CacheEvict(value = "userCache", allEntries = true)
public void refreshAllUser() {
}3. 方法執(zhí)行前刪除
@CacheEvict(value = "user", key = "#userId", beforeInvocation = true)
五、另外兩個常用注解
1. @CachePut
強制更新緩存,每次都會執(zhí)行方法體,適合實時更新緩存。
@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
userMapper.updateById(user);
return user;
}2. @Caching
組合多個緩存操作:
@Caching(
evict = {
@CacheEvict(value = "user", key = "#userId"),
@CacheEvict(value = "userOrder", key = "#userId")
}
)
public void deleteUser(Long userId) {
}六、模擬場景
場景 1:用戶詳情(典型查詢緩存)
@Cacheable(value = "userInfo", key = "#userId", unless = "#result == null")
public User getUser(Long userId) {
return userMapper.selectById(userId);
}場景 2:修改用戶 → 清理緩存
@CacheEvict(value = "userInfo", key = "#user.id")
public void updateUser(User user) {
userMapper.updateById(user);
}場景 3:刪除用戶 → 清理緩存
@CacheEvict(value = "userInfo", key = "#userId")
public void deleteUser(Long userId) {
userMapper.deleteById(userId);
}場景 4:商品列表緩存
@Cacheable(value = "productList", key = "#categoryId")
public List<Product> getProductList(Integer categoryId) {
return productMapper.selectByCategory(categoryId);
}場景 5:批量刷新商品緩存
@CacheEvict(value = "productList", allEntries = true)
public void refreshProduct() {
}場景 6:字典/配置(幾乎不變,長期緩存)
@Cacheable(value = "dictCache", key = "#dictType")
public List<Dict> getDict(String dictType) {
return dictMapper.selectByType(dictType);
}七、注意事項
1. 同類方法內(nèi)調(diào)用,注解失效
因為走了代理,同類內(nèi)部方法調(diào)用不經(jīng)過 AOP。
解決:抽取到獨立 Service 或自己注入自己。
2. 緩存 key 沖突
不同業(yè)務(wù)一定要用不同 value/cacheNames。
3. 緩存與數(shù)據(jù)庫不一致
增刪改必須配合 @CacheEvict 或 @CachePut。
4. 大數(shù)據(jù)量列表緩存
列表緩存容易占內(nèi)存,建議設(shè)置更短過期時間 + 分頁緩存。
5. null 值被緩存
用 unless = "#result == null" 避免。
6. 事務(wù)與緩存順序問題
建議事務(wù)提交后再清緩存,否則會出現(xiàn)“臟緩存”。
八、@Cacheable vs @CachePut vs @CacheEvict 總結(jié)
注解 | 作用 | 執(zhí)行方法體 | 典型場景 |
@Cacheable | 查 + 緩存 | 緩存不存在才執(zhí)行 | 查詢接口 |
@CachePut | 強制更新緩存 | 每次都執(zhí)行 | 實時同步 |
@CacheEvict | 刪除緩存 | 每次都執(zhí)行 | 增刪改 |
九、總結(jié)
SpringBoot 緩存注解是后端最實用的簡化技巧之一:
- 查詢用
@Cacheable - 更新刪除用
@CacheEvict - 實時同步用
@CachePut
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java數(shù)組中的元素刪除并實現(xiàn)向前移的代碼
這篇文章主要介紹了Java數(shù)組中的元素刪除并實現(xiàn)向前移的代碼的相關(guān)資料,需要的朋友可以參考下2016-05-05
Java中有界隊列的飽和策略(reject policy)原理解析
這篇文章主要介紹了Java中有界隊列的飽和策略(reject policy)原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-04-04
Java Hibernate中的持久化類和實體類關(guān)系
Hibernate是一種Java對象關(guān)系映射框架,通過持久化類將Java對象映射到數(shù)據(jù)庫表中。持久化類需要實現(xiàn)無參構(gòu)造器、具有標識屬性和使用注解或XML進行映射。Hibernate通過Session來管理對象的狀態(tài),包括臨時狀態(tài)、持久化狀態(tài)和游離狀態(tài)2023-04-04
idea中springboot項目連接數(shù)據(jù)庫報錯的原因解析
這篇文章主要介紹了idea中springboot項目連接數(shù)據(jù)庫報錯的原因解析,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12
Spring中@Configuration注解和@Component注解的區(qū)別詳解
這篇文章主要介紹了Spring中@Configuration注解和@Component注解的區(qū)別詳解,@Configuration 和 @Component 到底有何區(qū)別呢?我先通過如下一個案例,在不分析源碼的情況下,小伙伴們先來直觀感受一下這兩個之間的區(qū)別,需要的朋友可以參考下2023-09-09
springcloud使用Hystrix進行微服務(wù)降級管理
這篇文章主要介紹了springcloud使用Hystrix進行微服務(wù)降級管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-04-04

