使用Spring?Cache本地緩存示例代碼
一、Spring Cache簡(jiǎn)介
Spring Cache 是 Spring 框架提供的一套聲明式緩存抽象層,通過(guò)注解方式簡(jiǎn)化緩存操作,它通過(guò)在方法上添加注解(如 @Cacheable、@CacheEvict)來(lái)地管理緩存操作,無(wú)需手動(dòng)編寫(xiě)緩存邏輯。
它支持多種緩存實(shí)現(xiàn)(如 Caffeine、Redis、EhCache),并統(tǒng)一了緩存訪問(wèn)的 API。
這里需要注意兩點(diǎn):
- Spring Cache 只是一個(gè)聲明式的抽象緩存層,意思是它只提供了接口,不提供實(shí)現(xiàn)
- 具體的實(shí)現(xiàn)可以有很多,比如 Caffeine,Redis,EhCache 這些,只要實(shí)現(xiàn)了這些接口,就可以被 Spring Cache 使用。
核心特點(diǎn):
- 基于注解的聲明式緩存
- 支持 SpEL 表達(dá)式
- 自動(dòng)與 Spring 生態(tài)集成
- 支持條件緩存
二、基礎(chǔ)配置
1. 添加依賴
<!-- Spring Boot Cache Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- 如果使用Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 如果使用caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
2. 啟用緩存
在啟動(dòng)類(lèi)添加@EnableCaching注解:
@SpringBootApplication
@EnableCaching
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
3. 緩存配置方案
方案1:通過(guò) yml 配置文件
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=500,expireAfterWrite=60s
# 或者分開(kāi)配置
cache-names: users,products
caffeine.spec: # 全局默認(rèn)配置
maximumSize=1000,
expireAfterAccess=30m
方案2:自定義 Bean
@Configuration // 標(biāo)記這是一個(gè)Spring配置類(lèi)
public class CacheConfig {
/**
* 創(chuàng)建并配置Caffeine緩存管理器
*
* @return CacheManager 實(shí)例,用于管理應(yīng)用中所有緩存
*
* 主要配置參數(shù)說(shuō)明:
* - initialCapacity: 初始緩存空間大?。ㄌ嵘跏夹阅埽?
* - maximumSize: 緩存最大容量(基于條目數(shù))
* - expireAfterWrite: 寫(xiě)入后過(guò)期時(shí)間(數(shù)據(jù)一致性優(yōu)先場(chǎng)景)
* - recordStats: 開(kāi)啟統(tǒng)計(jì)功能(用于監(jiān)控和調(diào)優(yōu))
*/
@Bean
public CacheManager cacheManager() {
// 創(chuàng)建Caffeine緩存管理器實(shí)例
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 配置Caffeine緩存參數(shù)
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(100) // 初始容量100個(gè)條目
.maximumSize(1000) // 最大緩存1000個(gè)條目,超過(guò)后按LRU淘汰
.expireAfterWrite(10, TimeUnit.MINUTES) // 寫(xiě)入10分鐘后過(guò)期
.recordStats()); // 啟用緩存統(tǒng)計(jì)(命中率等)
return cacheManager;
}
/**
* 創(chuàng)建短期緩存實(shí)例(獨(dú)立于主緩存管理器)
*
* @return Cache 實(shí)例,適用于高頻訪問(wèn)的臨時(shí)數(shù)據(jù)
*
* 典型使用場(chǎng)景:
* - 高頻訪問(wèn)的臨時(shí)數(shù)據(jù)
* - 需要快速失效的驗(yàn)證碼等
* - 與其他緩存不同生命周期的數(shù)據(jù)
*/
@Bean(name = "shortTermCache") // 指定Bean名稱(chēng)便于按名稱(chēng)注入
public Cache shortTermCache() {
return Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES) // 1分鐘過(guò)期(短期存儲(chǔ))
.maximumSize(100) // 最大100個(gè)條目
.build(); // 構(gòu)建Cache實(shí)例
}
}
三、 緩存注解使用示例
@Cacheable:用于標(biāo)記方法,表示該方法將結(jié)果緩存起來(lái),下次調(diào)用時(shí)直接從緩存中獲取結(jié)果,而不需要重新執(zhí)行方法。@CacheEvict:用于標(biāo)記方法,表示該方法將清除緩存,通常用于刪除緩存。@CachePut:用于標(biāo)記方法,表示該方法將更新緩存,通常用于更新緩存。@Caching:用于組合多個(gè)緩存注解,可以同時(shí)使用多個(gè)緩存注解。@CacheConfig:用于標(biāo)記類(lèi),表示該類(lèi)中的所有方法將使用指定的緩存配置。
1.@Cacheable - 數(shù)據(jù)查詢緩存
/**
* 根據(jù)ID獲取用戶信息(帶緩存)
* @param id 用戶ID
* @return 用戶對(duì)象,如果不存在返回null
*
* @Cacheable 參數(shù)說(shuō)明:
* - value/cacheNames: 指定緩存名稱(chēng)(對(duì)應(yīng)Caffeine配置)
* - key: 緩存鍵,使用SpEL表達(dá)式(#參數(shù)名引用方法參數(shù))
* - unless: 條件表達(dá)式,當(dāng)結(jié)果滿足條件時(shí)不緩存
*/
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
log.info("執(zhí)行數(shù)據(jù)庫(kù)查詢,用戶ID: {}", id);
return userRepository.findById(id).orElse(null);
}
2.@CachePut - 更新數(shù)據(jù)并緩存
/**
* 更新用戶信息(同時(shí)更新緩存)
* @param user 用戶對(duì)象
* @return 更新后的用戶對(duì)象
*
* @CachePut 特點(diǎn):
* - 總是執(zhí)行方法體
* - 用返回值更新緩存
* - 適用于"先寫(xiě)后讀"場(chǎng)景
*/
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
log.info("更新用戶數(shù)據(jù): {}", user.getId());
return userRepository.save(user);
}
3.@CacheEvict - 刪除緩存
/**
* 刪除用戶(同時(shí)移除緩存)
* @param id 用戶ID
*
* @CacheEvict 參數(shù)說(shuō)明:
* - beforeInvocation: 是否在方法執(zhí)行前清除緩存(默認(rèn)false)
* - allEntries: 是否清空整個(gè)緩存區(qū)域(慎用)
*/
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
log.info("刪除用戶: {}", id);
userRepository.deleteById(id);
}
4. @Caching - 組合操作
組合操作允許在單個(gè)方法上同時(shí)使用多個(gè)緩存注解,以實(shí)現(xiàn)更復(fù)雜的緩存策略。
/**
* 更新用戶狀態(tài)(復(fù)雜緩存操作)
* @param userId 用戶ID
* @param status 新?tīng)顟B(tài)
*
* 典型場(chǎng)景:
* - 更新用戶緩存
* - 同時(shí)失效用戶列表緩存
*/
@Caching(
put = @CachePut(value = "users", key = "#userId"),
evict = @CacheEvict(value = "userList", allEntries = true)
)
public void updateUserStatus(Long userId, UserStatus status) {
log.info("更新用戶{}狀態(tài)為{}", userId, status);
userRepository.updateStatus(userId, status);
}
5. 條件緩存 (condition/unless)
條件緩存允許在緩存注解中添加 SpEL 條件表達(dá)式,以控制緩存的觸發(fā)時(shí)機(jī)。
/**
* 獲取用戶詳情(帶條件緩存)
* @param id 用戶ID
*
* 緩存條件說(shuō)明:
* - condition: 只有id>1000時(shí)才走緩存
* - unless: 結(jié)果中status=DELETED時(shí)不緩存
*/
@Cacheable(value = "users",
key = "#id",
condition = "#id > 1000",
unless = "#result != null && #result.status == T(com.example.UserStatus).DELETED")
public User getUserDetail(Long id) {
log.info("查詢用戶詳情: {}", id);
return userRepository.findDetailById(id);
}
6. 異步緩存加載
/**
* 獲取用戶訂單列表(異步緩存)
* @param userId 用戶ID
*
* @sync = true 表示:
* - 多線程并發(fā)時(shí),只有一個(gè)線程會(huì)執(zhí)行加載
* - 其他線程等待結(jié)果
*/
@Cacheable(value = "orders", key = "#userId", sync = true)
public List<Order> getUserOrders(Long userId) {
log.info("加載用戶{}訂單數(shù)據(jù)...", userId);
return orderService.getOrdersByUser(userId);
}
四、特殊場(chǎng)景處理
1. 緩存空值防御
/**
* 查詢用戶(防穿透處理)
* @param name 用戶名
*
* 特殊處理:
* - 對(duì)null結(jié)果也進(jìn)行緩存(特殊標(biāo)記對(duì)象)
* - 設(shè)置較短過(guò)期時(shí)間(配置文件中定義)
*/
@Cacheable(value = "usersByName",
key = "#name",
unless = "#result == null || #result == T(com.example.CacheConstants).NULL_OBJECT")
public User getUserByName(String name) {
User user = userRepository.findByName(name);
return user != null ? user : CacheConstants.NULL_OBJECT;
}
2. 復(fù)合緩存鍵
/**
* 獲取用戶在某系統(tǒng)的權(quán)限列表
* @param userId 用戶ID
* @param systemCode 系統(tǒng)編碼(如:"OA", "CRM"等)
* @return 權(quán)限字符串集合
*
* 緩存Key設(shè)計(jì)說(shuō)明:
* 1. 使用復(fù)合Key結(jié)構(gòu):`用戶ID_系統(tǒng)編碼`(如:123_OA)
* 2. 優(yōu)點(diǎn):
* - 避免不同系統(tǒng)權(quán)限緩存沖突
* - 支持按用戶+系統(tǒng)維度獨(dú)立管理緩存
* 3. 緩存條件:僅當(dāng)結(jié)果非空時(shí)緩存
*/
@Cacheable(value = "userPermissions",
key = "#userId + '_' + #systemCode",
unless = "#result == null || #result.isEmpty()")
public Set<String> getUserSystemPermissions(Long userId, String systemCode) {
log.debug("查詢用戶[{}]在系統(tǒng)[{}]的權(quán)限", userId, systemCode);
return permissionService.findPermissions(userId, systemCode);
}
/**
* 獲取用戶角色列表(帶枚舉參數(shù)的Key示例)
* @param userId 用戶ID
* @param roleType 角色類(lèi)型枚舉
*
* 枚舉類(lèi)型處理技巧:
* 1. 調(diào)用枚舉的name()方法轉(zhuǎn)換為字符串
* 2. 最終Key格式:`userId:roleType`(如:123:ADMIN)
*/
@Cacheable(value = "userRoles",
key = "#userId + ':' + #roleType.name()")
public List<Role> getUserRoles(Long userId, RoleType roleType) {
return roleService.findByUserAndType(userId, roleType);
}
五、經(jīng)驗(yàn)之談
- 推薦為每個(gè)
@Cacheable方法添加unless條件防御null值 - 業(yè)務(wù)更新方法建議同時(shí)使用
@CachePut和@CacheEvict - 高頻訪問(wèn)數(shù)據(jù)考慮設(shè)置
sync=true
總結(jié)
到此這篇關(guān)于使用Spring Cache本地緩存的文章就介紹到這了,更多相關(guān)Spring Cache本地緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)文件批量重命名具體實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)文件批量重命名具體實(shí)例,需要的朋友可以參考下2014-02-02
MyBatis的collection和association的使用解讀
這篇文章主要介紹了MyBatis的collection和association的使用解讀2023-12-12
java實(shí)現(xiàn)簡(jiǎn)單的圖書(shū)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單的圖書(shū)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Java中的break和continue關(guān)鍵字的使用方法總結(jié)
下面小編就為大家?guī)?lái)一篇Java中的break和continue關(guān)鍵字的使用方法總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11
IntelliJ IDEA中程序包org.slf4j找不到的解決
這篇文章主要介紹了IntelliJ IDEA中程序包org.slf4j找不到的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Spring事務(wù)失效場(chǎng)景及解決過(guò)程
這篇文章主要介紹了Spring事務(wù)失效場(chǎng)景及解決過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-07-07
Java Socket一對(duì)多通信實(shí)現(xiàn)之并發(fā)處理方式
這篇文章主要介紹了Java Socket一對(duì)多通信實(shí)現(xiàn)之并發(fā)處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
詳解Java并發(fā)包中線程池ThreadPoolExecutor
ThreadPoolExecutor是Java語(yǔ)言對(duì)于線程池的實(shí)現(xiàn)。線程池技術(shù)使線程在使用完畢后不回收而是重復(fù)利用。如果線程能夠復(fù)用,那么我們就可以使用固定數(shù)量的線程來(lái)解決并發(fā)問(wèn)題,這樣一來(lái)不僅節(jié)約了系統(tǒng)資源,而且也會(huì)減少線程上下文切換的開(kāi)銷(xiāo)2021-06-06
IntelliJ IDEA本地代碼提交到github網(wǎng)站不顯示與本地不同步問(wèn)題的解決辦法
今天小編就為大家分享一篇關(guān)于IntelliJ IDEA本地代碼提交到github網(wǎng)站不顯示與本地不同步問(wèn)題的解決辦法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-10-10

