SpringBoot?集成Caffeine實(shí)現(xiàn)一級(jí)緩存及常遇到場(chǎng)景
SpeingBoot 集成Caffeine實(shí)現(xiàn)一級(jí)緩存使我們經(jīng)常遇到的場(chǎng)景。今天我們具體分享一下:
首先 Caffeine 作為一級(jí)緩存,它是 Spring 5.x 默認(rèn)的本地緩存實(shí)現(xiàn),性能優(yōu)于 Guava Cache,且支持過期時(shí)間設(shè)置。緩存執(zhí)行的流程圖如下:

1、pom文件引入包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version> <!-- 兼容 Spring Boot 2.3.5 的版本 -->
</dependency>
2、換成配置類
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching
public class CacheConfig {
/**
Caffeine 配置參數(shù):
expireAfterWrite:寫入后多久過期
expireAfterAccess:最后訪問后多久過期
maximumSize:緩存的最大元素?cái)?shù)量
weakKeys/weakValues:使用弱引用,支持垃圾回收
**/
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)//可以選多種時(shí)間
.maximumSize(10));//最大緩存?zhèn)€數(shù)
// 配置特定緩存(超時(shí)時(shí)間5分鐘,到時(shí)間自動(dòng)置為null)
cacheManager.setCacheNames(java.util.Arrays.asList("timeoutParam"));
return cacheManager;
}
}1)配置多個(gè)緩存
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
// 配置不同的緩存區(qū)域(可根據(jù)業(yè)務(wù)需求設(shè)置不同的超時(shí)時(shí)間)
cacheManager.setCaches(Arrays.asList(
new CaffeineCache("timeoutParams",
Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES) // 5分鐘過期
.maximumSize(100)
.build()
),
new CaffeineCache("longTermCache",
Caffeine.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS) // 1小時(shí)過期
.maximumSize(1000)
.build()
)
));
return cacheManager;
}不同緩存數(shù)據(jù)的超時(shí)時(shí)間可能不一樣,因此需要設(shè)置不同的緩存。
3、業(yè)務(wù)層實(shí)現(xiàn)
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
@Service
public class MyService {
// 獲取參數(shù)(從緩存讀取,若無則執(zhí)行方法并緩存結(jié)果)unless:條件表達(dá)式,滿足條件則不緩存(如 #result == null)
@Cacheable(value = "timeoutParams", key = "#paramName")
public String getParam(String paramName) {
// 模擬從數(shù)據(jù)庫(kù)或其他數(shù)據(jù)源獲取
System.out.println("從數(shù)據(jù)源加載參數(shù): " + paramName);
return loadParamFromDB(paramName);
}
// 更新參數(shù)(強(qiáng)制刷新緩存,即使緩存存在)
@CachePut(value = "timeoutParams", key = "#paramName")
public String updateParam(String paramName, String newValue) {
// 保存到數(shù)據(jù)庫(kù)
saveParamToDB(paramName, newValue);
return newValue;
}
// 清除緩存:調(diào)用此方法后,指定緩存鍵將被刪除
@CacheEvict(value = "timeoutParams", key = "#paramName")
public void clearParam(String paramName) {
System.out.println("清除緩存: " + paramName);
// 通常無需方法體,僅用于觸發(fā)緩存清除
}
// 模擬數(shù)據(jù)庫(kù)操作
private String loadParamFromDB(String paramName) {
// 實(shí)際項(xiàng)目中從數(shù)據(jù)庫(kù)或其他數(shù)據(jù)源獲取
return "默認(rèn)值";
}
private void saveParamToDB(String paramName, String value) {
// 實(shí)際項(xiàng)目中保存到數(shù)據(jù)庫(kù)
}
}4、控制層調(diào)用
// 第一次調(diào)用,會(huì)從數(shù)據(jù)源加載
String value1 = myService.getParam("testParam");
System.out.println("第一次獲取: " + value1);
// 第二次調(diào)用,會(huì)從緩存獲取
String value2 = myService.getParam("testParam");
System.out.println("第二次獲取: " + value2);5、通過key獲取數(shù)據(jù)
1)通過key手動(dòng)插入
@Service
public class ManualCacheService {
@Autowired
private CacheManager cacheManager;
public void initCacheManually(String key, Object value) {
Cache cache = cacheManager.getCache("paramCache");
if (cache != null) {
cache.put(key, value); // 手動(dòng)放入緩存
System.out.println("手動(dòng)初始化緩存: " + key + " = " + value);
}
}
}2)手動(dòng)獲取
@Service
public class CacheAccessService {
@Autowired
private CacheManager cacheManager;
public Object getValueManually(String key) {
Cache cache = cacheManager.getCache("paramCache");
if (cache != null) {
Cache.ValueWrapper wrapper = cache.get(key);
return wrapper != null ? wrapper.get() : null;
}
return null;
}
}6、通過注解獲取數(shù)據(jù)(注意偽代碼)
@Service
public class JTServiceImpl {
@Service
public void someMethod() {
// 自調(diào)用 - 導(dǎo)致緩存失效
String token = this.getGlobalToken("token123");
}
@Cacheable(value = "timeoutGlobalToken", key = "#globalToken")
public String getGlobalToken(String globalToken) {
// 實(shí)際獲取token的邏輯
}
}注意:注解失效原因及解決方案
原因分析:
Spring AOP 工作原理:
Spring 的緩存功能(包括
@Cacheable)是基于 AOP 代理實(shí)現(xiàn)的當(dāng)調(diào)用
@Cacheable方法時(shí),實(shí)際調(diào)用的是代理對(duì)象的方法,不是原始對(duì)象的方法
自調(diào)用問題(Self-Invocation):
當(dāng)同一個(gè)類中的方法 A 直接調(diào)用方法 B(帶
@Cacheable注解)時(shí)調(diào)用發(fā)生在原始對(duì)象內(nèi)部,繞過了 Spring 代理
導(dǎo)致
@Cacheable注解完全失效
解決方案:將緩存方法移到另一個(gè)Service中
// 新建專門處理緩存的服務(wù)
@Service
public class TokenCacheService {
@Cacheable(value = "timeoutGlobalToken", key = "#globalToken")
public String getGlobalToken(String globalToken) {
// 實(shí)際獲取token的邏輯
return fetchTokenFromSource(globalToken);
}
private String fetchTokenFromSource(String globalToken) {
// 從數(shù)據(jù)庫(kù)/API獲取token的實(shí)現(xiàn)
}
}調(diào)用方:
// 原服務(wù)調(diào)用緩存服務(wù)
@Service
public class JTServiceImpl {
@Autowired
private TokenCacheService tokenCacheService;
public void businessMethod() {
// 跨類調(diào)用 - 觸發(fā)緩存
String token = tokenCacheService.getGlobalToken("token123");
}
}這是一個(gè)比較常用的解決方案。
8、獲取所有緩存信息
@GetMapping("/cache/param")
public Map<Object, Object> getCacheEntries() {
Cache cache = cacheManager.getCache("paramCache");
if (cache == null) {
return Collections.emptyMap();
}
com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) cache.getNativeCache();
return new HashMap<>(nativeCache.asMap());
}到此,SpringBoot 集成Caffeine實(shí)現(xiàn)一級(jí)緩存分享完成,下篇我們會(huì)繼續(xù)分享此種緩存實(shí)現(xiàn)的細(xì)節(jié),敬請(qǐng)期待!
到此這篇關(guān)于SpringBoot 集成Caffeine實(shí)現(xiàn)一級(jí)緩存的文章就介紹到這了,更多相關(guān)SpringBoot Caffeine一級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決mybatis-plus自動(dòng)配置的mapper.xml與java接口映射問題
這篇文章主要介紹了解決mybatis-plus自動(dòng)配置的mapper.xml與java接口映射問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java的Spring框架中DAO數(shù)據(jù)訪問對(duì)象的使用示例
這篇文章主要介紹了Java的Spring框架中DAO數(shù)據(jù)訪問對(duì)象的使用示例,分為在Spring中DOA與JDBC以及與Hibernate的配合使用兩種情況來進(jìn)行演示,需要的朋友可以參考下2016-03-03
java Hibernate 一對(duì)多自身關(guān)聯(lián)問題
formBean在提交表單的時(shí)候,域中數(shù)據(jù)庫(kù)在下一次中仍然保留引起的,struts formBean 默認(rèn)的scope為session,手動(dòng)設(shè)置為request,就好了2008-07-07
SpringBoot結(jié)合Ajax實(shí)現(xiàn)登錄頁(yè)面實(shí)例
大家好,本篇文章主要講的是SpringBoot結(jié)合Ajax實(shí)現(xiàn)登錄頁(yè)面實(shí)例,感興趣的同學(xué)趕快來看一看,對(duì)你有幫助的話記得收藏一下2022-02-02
并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語(yǔ)義
這篇文章主要介紹了并發(fā)編程之Java內(nèi)存模型volatile的內(nèi)存語(yǔ)義,理解volatile特性的一個(gè)好辦法是把對(duì)volatile變量的單個(gè)讀/寫,看成是使用同一個(gè)鎖對(duì)單個(gè)讀/寫操作做了同步。下面我們一起進(jìn)入文章看看具體例子吧,需要的小伙伴可以參考下2021-11-11

