Spring Cache + Caffeine的整合與使用示例詳解
前言
對(duì)于一些項(xiàng)目里需要對(duì)數(shù)據(jù)庫(kù)里的某些數(shù)據(jù)一直重復(fù)請(qǐng)求的,且這些數(shù)據(jù)基本是固定的,在這種情況下,可以借助簡(jiǎn)單使用本地緩存來(lái)緩存這些數(shù)據(jù)。這些介紹一下Spring Cache和Caffeine的使用。
引入依賴和CacheConfig
在pom文件里面引入下面的依賴:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>在啟動(dòng)類上加上@EnableCaching的注解
@EnableCaching
public class SpringBootApplication{
}新建一個(gè)CacheConfig類
@Configuration
public class CacheConfig {
/********************************
* @function : 生成緩存管理器
* @parameter : []
* @return : org.springframework.cache.CacheManager
* @date : 2023/12/13 14:46
********************************/
@Primary
@Bean("customCacheManager")
public CacheManager customCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
List<Cache> cacheList = new ArrayList<>();
cacheList.add(customCache());
simpleCacheManager.setCaches(cacheList);
return simpleCacheManager;
}
/********************************
* @function : 生成自定義緩存容器
* @parameter : []
* @return : org.springframework.cache.Cache
* @date : 2023/12/13 14:46
********************************/
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.build(), true);
}
}這里customCache()方法我并沒(méi)有設(shè)置相關(guān)過(guò)期時(shí)間和最大值,不設(shè)置會(huì)導(dǎo)致沒(méi)有默認(rèn)過(guò)期時(shí)間和最大值。如果需要設(shè)置可以參考下面的寫法
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.maximumSize(100)
.initialCapacity(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build(),
true);
}CaffeineCache參數(shù)的講解
- "customCache": 這是緩存的名稱。在應(yīng)用程序中,你可以通過(guò)這個(gè)名稱來(lái)獲取對(duì)應(yīng)的緩存實(shí)例。
Caffeine.newBuilder(): 這是創(chuàng)建Caffeine緩存實(shí)例的起始點(diǎn)。newBuilder()返回一個(gè)Caffeine構(gòu)建器對(duì)象,用于配置和定制緩存的各種屬性。.maximumSize(100): 這是設(shè)置緩存的最大容量,即緩存可以容納的最大條目數(shù)。在這個(gè)例子中,緩存的最大容量被設(shè)置為100。.initialCapacity(100): 這是設(shè)置緩存的初始容量,即在緩存初始化時(shí)分配的內(nèi)部數(shù)據(jù)結(jié)構(gòu)的初始大小。在這個(gè)例子中,初始容量被設(shè)置為100。.expireAfterWrite(10, TimeUnit.MINUTES): 這是設(shè)置緩存項(xiàng)在被寫入后的過(guò)期時(shí)間。在這個(gè)例子中,緩存項(xiàng)將在被寫入后的10分鐘內(nèi)過(guò)期。.recordStats(): 這是啟用緩存統(tǒng)計(jì)信息的選項(xiàng)。啟用后,你可以從緩存實(shí)例中獲取有關(guān)緩存使用情況的統(tǒng)計(jì)信息,例如命中率、加載次數(shù)等。
使用中,對(duì)過(guò)期策略的使用會(huì)比較重要,對(duì)于過(guò)期的策略有:
1.寫入后過(guò)期 (expireAfterWrite): 緩存項(xiàng)被寫入后的一段時(shí)間內(nèi)過(guò)期。可以通過(guò)以下方式配置:
Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
在上述示例中,緩存項(xiàng)將在被寫入后的10分鐘內(nèi)過(guò)期。
2.訪問(wèn)后過(guò)期 (expireAfterAccess): 緩存項(xiàng)在一段時(shí)間內(nèi)沒(méi)有被訪問(wèn)后過(guò)期。可以通過(guò)以下方式配置:
Caffeine.newBuilder()
.expireAfterAccess(15, TimeUnit.MINUTES)
.build();在上述示例中,緩存項(xiàng)將在最后一次訪問(wèn)后的15分鐘內(nèi)過(guò)期。
3.定時(shí)過(guò)期 (expireAfter): 緩存項(xiàng)在指定的固定時(shí)間內(nèi)過(guò)期,不考慮寫入或訪問(wèn)??梢酝ㄟ^(guò)以下方式配置:
Caffeine.newBuilder()
.expireAfter(1, TimeUnit.HOURS)
.build();在上述示例中,緩存項(xiàng)將在創(chuàng)建后的1小時(shí)內(nèi)過(guò)期。
這些過(guò)期定時(shí)策略可以根據(jù)具體的使用場(chǎng)景和需求進(jìn)行組合或選擇。
上面不同寫法將會(huì)導(dǎo)致生成不同的localcache實(shí)現(xiàn)類,可以在build方法中看到:

進(jìn)入isBounded()方法:

如果使用緩存會(huì)調(diào)用localcache的get方法,最后進(jìn)入computeIfAbsent()方法,對(duì)比上面兩個(gè)實(shí)現(xiàn)類的實(shí)現(xiàn),先是BoundedLocalCache:

UnboundedLocalCache:

下面這個(gè)并不會(huì)去檢查是否過(guò)期。
使用示范
在MVC的使用,可以將緩存的注解標(biāo)識(shí)于service層:
@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {
@Cacheable(key = "#root.methodName + #sendCardName")
public int getSlotCount(String sendCardName) {
..方法體
return calCard.getSlotCount();
}
...
@CachePut(key = "#param")
public String updateCache(String param) {
// 對(duì)數(shù)據(jù)庫(kù)更新某個(gè)值
return updatedValue;
}
@CacheEvict(key = "#param")
public void evictCache(String param) {
// 對(duì)數(shù)據(jù)庫(kù)刪除某個(gè)值
}
}使用到的注解解析:
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager") 標(biāo)注在類上,cacheNames表示當(dāng)前使用的緩存名字,在創(chuàng)建緩存的時(shí)候有指定,第二個(gè)cacheManager是創(chuàng)建cacheManager管理器時(shí)指定的Bean名稱,這里是 @Bean("customCacheManager")。
@Cacheable 是Spring框架中用于聲明緩存規(guī)則的注解之一。它通常用于標(biāo)記在方法上,以指示Spring在執(zhí)行方法前先檢查緩存,如果緩存中已有數(shù)據(jù),則直接返回緩存中的數(shù)據(jù),而不執(zhí)行方法體。如果緩存中沒(méi)有數(shù)據(jù),則執(zhí)行方法體,并將方法的返回值存入緩存。
以下是 以@Cacheable 注解為例的主要參數(shù)介紹和使用方式:
1.value(或 cacheNames): 指定緩存的名稱,可以指定一個(gè)或多個(gè)緩存。如果指定多個(gè)緩存,Spring會(huì)依次檢查緩存,直到找到第一個(gè)有數(shù)據(jù)的緩存或全部檢查完畢。示例:
@Cacheable(value = "myCache")
public String getCachedData() {
// 方法體
}2.key: 指定緩存項(xiàng)的鍵。默認(rèn)情況下,Spring會(huì)使用方法的參數(shù)作為鍵,但你也可以通過(guò) key 屬性指定自定義的緩存鍵。示例:
@Cacheable(value = "myCache", key = "#param")
public String getCachedData(String param) {
// 方法體
}3.condition: 指定條件表達(dá)式,只有當(dāng)條件滿足時(shí)才會(huì)緩存。示例:
@Cacheable(value = "myCache", condition = "#result != null")
public String getCachedData() {
// 方法體
}4.unless: 指定一個(gè)條件表達(dá)式,當(dāng)條件為 true 時(shí),不會(huì)將結(jié)果放入緩存。示例:
@Cacheable(value = "myCache", unless = "#result == null")
public String getCachedData() {
// 方法體
}5.keyGenerator: 指定自定義的緩存鍵生成器。這個(gè)屬性允許你提供一個(gè)實(shí)現(xiàn)了 org.springframework.cache.interceptor.KeyGenerator 接口的類,用于生成緩存鍵。示例:
@Cacheable(value = "myCache", keyGenerator = "customKeyGenerator")
public String getCachedData() {
// 方法體
}6.sync: 是否啟用同步模式。如果設(shè)置為 true,可以解決并發(fā)查的問(wèn)題,Spring會(huì)在調(diào)用方法時(shí)鎖定緩存,防止多個(gè)線程同時(shí)訪問(wèn)數(shù)據(jù)庫(kù)。默認(rèn)為 false。示例:
@Cacheable(value = "myCache", sync = true)
public String getCachedData() {
// 方法體
}這些是 @Cacheable 注解的一些常用參數(shù)??梢愿鶕?jù)實(shí)際需要選擇合適的參數(shù)來(lái)定義緩存規(guī)則。
在Spring中,除了 @Cacheable,另外一些注解及其簡(jiǎn)要介紹:
1.@CacheEvict: 用于從緩存中移除數(shù)據(jù)。通常用于在方法執(zhí)行后清空指定緩存。示例:
@CacheEvict(value = "myCache", key = "#param")
public void evictCache(String param) {
// 方法體
}2.@CachePut: 用于將方法的返回值更新到緩存中,常用于更新緩存而不影響方法的執(zhí)行。示例:
@CachePut(value = "myCache", key = "#param")
public String updateCache(String param) {
// 方法體
return updatedValue;
}3.@Caching: 用于將多個(gè)緩存相關(guān)的注解組合在一起,實(shí)現(xiàn)復(fù)雜的緩存操作。示例:
@Caching(
evict = {@CacheEvict(value = "cache1", key = "#param1")},
put = {@CachePut(value = "cache2", key = "#param2")}
)
public String complexCacheOperation(String param1, String param2) {
// 方法體
}4.@CacheConfig: 用于在類級(jí)別配置緩存的一些公共屬性,避免在每個(gè)方法上都重復(fù)指定相同的緩存名稱等信息。示例:
@CacheConfig(cacheNames = "commonCache")
public class MyService {
@Cacheable
public String getCachedData(String param) {
// 方法體
}
}這些注解可以單獨(dú)使用,也可以結(jié)合使用,以滿足不同的緩存需求。
清空緩存的方法
清空所有緩存,可以不指定 value 和 key,如下所示:
@CacheEvict(allEntries = true)
public void evictAllCaches() {
// 方法體
}在這個(gè)例子中,allEntries = true 表示清空所有緩存。
如果你想根據(jù)某個(gè)條件來(lái)判斷是否清空緩存,可以使用 condition 屬性,例如:
@CacheEvict(value = "myCache", key = "#param", condition = "#param != 'noEviction'")
public void evictCacheConditionally(String param) {
// 方法體
}在上述例子中,只有當(dāng) param 不等于 'noEviction' 時(shí)才會(huì)執(zhí)行緩存清空操作。
除了 @CacheEvict,在一些特定場(chǎng)景下,@CachePut 也可以被用來(lái)“清空”緩存,因?yàn)樗鼘⒎椒ǖ姆祷刂捣湃刖彺妫绻祷刂禐?null,相當(dāng)于移除緩存項(xiàng)。這種方式通常在更新操作時(shí)使用。
注意事項(xiàng)
如下圖代碼所示,如果在updateCache方法又調(diào)用了同個(gè)類里面的getSlotCount()方法,是不會(huì)使用到緩存的,這是因?yàn)榫彺娴膶?shí)現(xiàn)是通過(guò)AOP實(shí)現(xiàn),在同個(gè)類里面調(diào)用方法,實(shí)際是通過(guò)this來(lái)調(diào),不會(huì)調(diào)用到代理對(duì)象,因此相當(dāng)于@Cacheable注解在這種情況是不生效的。
@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {
@Cacheable(key = "#root.methodName + #sendCardName")
public int getSlotCount(String sendCardName) {
..方法體
return calCard.getSlotCount();
}
...
@CachePut(key = "#param")
public String updateCache(String param) {
getSlotCount("xx");
// 對(duì)數(shù)據(jù)庫(kù)更新某個(gè)值
return updatedValue;
}
}到此這篇關(guān)于Spring Cache + Caffeine的整合與使用的文章就介紹到這了,更多相關(guān)Spring Cache Caffeine內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring MVC之WebApplicationContext_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了Spring MVC之WebApplicationContext的相關(guān)資料,需要的朋友可以參考下2017-08-08
Spring Boot 使用 Swagger 構(gòu)建 RestAPI 接口文檔
這篇文章主要介紹了Spring Boot 使用 Swagger 構(gòu)建 RestAPI 接口文檔,幫助大家更好的理解和使用Spring Boot框架,感興趣的朋友可以了解下2020-10-10
java 中序列化NotSerializableException問(wèn)題解決辦法
這篇文章主要介紹了java 中序列化NotSerializableException問(wèn)題解決辦法的相關(guān)資料,這里對(duì)序列化問(wèn)題進(jìn)行描述說(shuō)明,并提供解決辦法,希望能幫助到大家,需要的朋友可以參考下2017-08-08

