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

