SpringBoot 緩存 Caffeine使用解析
Redis和Caffeine的區(qū)別
相同點(diǎn)
- 兩個都是緩存的方式
不同點(diǎn)
- redis是分布式緩存,通過網(wǎng)絡(luò)將數(shù)據(jù)存儲到redis服務(wù)器內(nèi)存里
- caffeine是將數(shù)據(jù)存儲在本地應(yīng)用里
- caffeine和redis相比,沒有了網(wǎng)絡(luò)IO上的消耗
聯(lián)系
- 一般將兩者結(jié)合起來,形成一二級緩存。
- 使用流程大致如下:
- 先去一級緩存中查找數(shù)據(jù)(caffeine-本地應(yīng)用內(nèi)),
- 如果沒有的話,去二級緩存中查找數(shù)據(jù)(redis-內(nèi)存),
- 再沒有,再去數(shù)據(jù)庫中查找數(shù)據(jù)(數(shù)據(jù)庫-磁盤)
Spring Boot 緩存 Caffeine使用
1.需要添加的依賴
<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.8.6</version> </dependency>
2.配置
在SpringBoot中配置Caffeine,控制緩存行為(例如過期時間,緩存大小限制等)
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 CaffeinConfig { @Bean //配置Caffeine緩存行為(例如到期,緩存大小限制等) public Caffeine caffeineConfig() { Caffeine caffeine = Caffeine.newBuilder() .expireAfterWrite(60, TimeUnit.MINUTES) .maximumSize(1000); return caffeine; } @Bean public CacheManager cacheManager(Caffeine caffeine) { CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager(); caffeineCacheManager.setCaffeine(caffeine); return caffeineCacheManager; } }
Caffeine配置說明:
initialCapacity=[integer]
:初始的緩存空間大小
maximumSize=[long]
:緩存的最大條數(shù)
maximumWeight=[long]
:緩存的最大權(quán)重
expireAfterAccess=[duration]
:最后一次寫入或訪問后經(jīng)過固定時間過期
expireAfterWrite=[duration]
:最后一次寫入后經(jīng)過固定時間過期
refreshAfterWrite=[duration]
:創(chuàng)建緩存或者最近一次更新緩存后經(jīng)過固定的時間間隔,刷新緩存
recordStats
:開發(fā)統(tǒng)計(jì)功能
注意:
expireAfterWrite和expireAfterAccess同時存在時,以expireAfterWrite為準(zhǔn)。
maximumSize和maximumWeight不可以同時使用
3.使用Caffeine緩存
示例1:
使用 @Cacheable(cacheNames = “xxx”) 或 @Cacheable(value = “xxx”) 注解在方法上。
@Cacheable(value = "caffeinSet_Value") public String caffeinSetValue(Integer number) { String str = number % 2 == 0 ? number + "是偶數(shù)" : number + "是奇數(shù)"; return str; }
說明: 每次執(zhí)行方法 caffeinSetValue 時,會先去 caffeinSet_Value 緩存里根據(jù)傳入的 number 查找有沒有匹配的緩存,有則直接返回結(jié)果;沒有則執(zhí)行方法,執(zhí)行完后將結(jié)果加入緩存里,下次如果匹配直接返回。
示例2:
當(dāng)有多個參數(shù)時,@Cacheable 注解里可以使用 key 來選擇參數(shù)進(jìn)行判斷緩存是否存在。可以使用 condition 來進(jìn)行條件篩選,只有滿足條件的才會加入緩存。
/** * number為偶數(shù)時才會緩存,緩存的key是傳入的number值 */ @Cacheable(value = "caffeinSet_Value", key = "#number", condition = "#number%2==0") public String caffeinSetValue(Integer number,String st) { String str = number % 2 == 0 ? number + "是偶數(shù)" : number + "是奇數(shù)"; return str+st; }
說明: 假如傳入的參數(shù) number 是2,首先判斷 caffeinSet_Value 緩存里有沒有 key 是2的,有則直接回結(jié)果。沒有則執(zhí)行方法,因?yàn)闈M足 condition 的條件則最后將結(jié)果加入緩存。
假如傳入的參數(shù) number 是奇數(shù),則每次都會執(zhí)行方法,因?yàn)椴粷M足 condition ,不會被加入緩存。
示例3:
@Cacheable(value = "caffeinSet_Value", key = "#student.name", condition = "#student.age>10") public Student caffeinSetValue(Student student,Integer number) { System.out.println(11111); return student; }
說明: 根據(jù)student對象里的name去caffeinSetValue緩存里查找。只有student對象里的age大于10的時候才會緩存結(jié)果。
注意:
一個方法A調(diào)同一個類里的另一個有緩存注解的方法B,這樣是不走緩存的。
例如在同一個CaffeinConsumer 類里面 invalidCache 調(diào)用 caffeinSetValue,是不走緩存的,緩存是不生效的;
@Service public class CaffeinConsumer { public String invalidCache(Integer number,String st) { String str = caffeinSetValue(number,st); return str; } /** * number為偶數(shù)時才會緩存,緩存的key是傳入的number值 */ @Cacheable(value = "caffeinSet_Value", key = "#number", condition = "#number%2==0") public String caffeinSetValue(Integer number,String st) { String str = number % 2 == 0 ? number + "是偶數(shù)" : number + "是奇數(shù)"; return str+st; } }
解決方案:
1.不使用注解的方式,直接取 Ehcache 的 CacheManger 對象,把需要緩存的數(shù)據(jù)放到里面,類似于使用 Map,緩存的邏輯自己控制;或者可以使用redis的緩存方式去添加緩存;
2.把方法A和方法B放到兩個不同的類里面,例如:如果兩個方法都在同一個service接口里,把方法B放到另一個service里面,這樣在A方法里調(diào)B方法,就可以使用B方法的緩存。
Caffeine其他常用注解
1.@CachePut:
被@CachePut標(biāo)注的方法在執(zhí)行前不會去檢查緩存中是否存在之前執(zhí)行過的結(jié)果,而是每次都會執(zhí)行該方法,并將執(zhí)行結(jié)果以鍵值對的形式存入指定的緩存中。
2.@CacheEvict:
@CacheEvict是用來標(biāo)注在需要清除緩存元素的方法或類上的。當(dāng)標(biāo)記在一個類上時表示其中所有的方法的執(zhí)行都會觸發(fā)緩存的清除操作。
@CacheEvict可以指定的屬性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的語義與@Cacheable對應(yīng)的屬性類似。
即value表示清除操作是發(fā)生在哪些Cache上的(對應(yīng)Cache的名稱);key表示需要清除的是哪個key,如未指定則會使用默認(rèn)策略生成的key;condition表示清除操作發(fā)生的條件。如果要清除所有緩存可使用屬性 allEntries=true
手動添加、獲取、刪除緩存
上面的一些示例通過注解來進(jìn)行緩存操作,有時候我們需要在一些方法里對緩存進(jìn)行操作增刪改查:
1.從緩存中獲取數(shù)據(jù)
假設(shè)上面示例2中向名為 “caffeinSet_Value”的緩存里加入的鍵是8,值是"8是偶數(shù)!!"。
下面手動獲取此緩存:
@Autowired CacheManager cacheManager; public String caffeinGetValue() { Cache cache = cacheManager.getCache("caffeinSet_Value"); //獲取緩存名稱。name為caffeinSetValue String name = cache.getName(); //獲取caffeinSetValue緩存里建是8的緩存 Cache.ValueWrapper value = cache.get(8); String str =""; if (null != value) { //獲取值,8是偶數(shù)!! str = String.valueOf(value.get()); } return str; }
2.向緩存中添加數(shù)據(jù)
@Autowired CacheManager cacheManager; public String caffeinPutValue() { Cache cache = cacheManager.getCache("caffeinSet_Value"); //獲取緩存名稱。name為caffeinSetValue String name = cache.getName(); /* //向緩存中put數(shù)據(jù)。如果不存在key是20的才會加入 cache.putIfAbsent(number, "添加測試"); */ //向緩存中put數(shù)據(jù)。如果存在key是20的會覆蓋原來的數(shù)據(jù) cache.put(20,"20是偶數(shù)??!"); return "成功"; }
3.刪除緩存中的數(shù)據(jù)
刪除caffeinSet_Value緩存中的某條緩存:
@Autowired CacheManager cacheManager; public String caffeinDeleteValue() { Cache cache = cacheManager.getCache("caffeinSet_Value"); //獲取緩存名稱。name為caffeinSetValue String name = cache.getName(); //只有20這條數(shù)據(jù)存在才會刪除 boolean bo = cache.evictIfPresent(20); return String.valueOf(bo); }
刪除caffeinSet_Value緩存中的所有緩存:
@Autowired CacheManager cacheManager; public String caffeinDeleteAllValue() { Cache cache = cacheManager.getCache("caffeinSet_Value"); //獲取緩存名稱。name為caffeinSetValue String name = cache.getName(); //刪除caffeinSet_Value中的所有緩存 boolean bo = cache.invalidate(); return String.valueOf(bo); }
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringCloud中的Feign服務(wù)間的調(diào)用詳解
這篇文章主要介紹了SpringCloud中的Feign服務(wù)間的調(diào)用詳解,Feign 是一個聲明式的 REST 客戶端,它能讓 REST 調(diào)用更加簡單,Feign 供了 HTTP 請求的模板,通過編寫簡單的接口和插入注解,就可以定義好 HTTP 請求的參數(shù)、格式、地址等信息,需要的朋友可以參考下2024-01-01SpringMVC之AbstractAnnotationConfigDispatcherSer解讀
這篇文章主要介紹了SpringMVC之AbstractAnnotationConfigDispatcherSer,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Java和C++通過new創(chuàng)建的對象有何區(qū)別?
Java和C++都是面向?qū)ο蟮木幊陶Z言,然而Java和C++在創(chuàng)建對象時卻存在不同的方式,由于方式的不同導(dǎo)致在內(nèi)存中管理的不同。這篇文章主要給大家介紹了關(guān)于Java和C++通過new創(chuàng)建對象區(qū)別的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-11-11Java使用poi組件導(dǎo)出Excel格式數(shù)據(jù)
這篇文章主要介紹了Java使用poi組件導(dǎo)出Excel格式數(shù)據(jù),需要的朋友可以參考下2020-02-02spring AOP的Around增強(qiáng)實(shí)現(xiàn)方法分析
這篇文章主要介紹了spring AOP的Around增強(qiáng)實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了spring面向切面AOP的Around增強(qiáng)具體步驟與相關(guān)操作方法,需要的朋友可以參考下2020-01-01SpringBoot注冊Filter的兩種實(shí)現(xiàn)方式
這篇文章主要介紹了SpringBoot注冊Filter的兩種實(shí)現(xiàn)方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08