序
Caffeine的坑,多的不想說,唯一想說的就是如果配置了特異性緩存,後面就不再預設建立,這點就比較不太友好。不過也不是什麼特别大的問題,最多就是我自行多配置1個預設配置的呗。
一、引入jar支援
<!-- 緩存支援 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Caffeine支援 -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
二、緩存配置
import cn.hutool.core.collection.CollectionUtil;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Caffeine自定義緩存配置(本地緩存)
*
* @author zhengwen
**/
@Slf4j
@Configuration
public class CaffeineSpecialCacheConfig extends CachingConfigurerSupport {
/**
* 自定義字典緩存名稱(本地緩存)
*/
@Value("${spring.cache.caffeine.special.caches}")
private String specialCachesStr;
@Bean
public CacheManager caffeineCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
//先處理已經有的緩存,防止破壞其他全局緩存(啟動的時候這個配置類就會走,是不是有點多餘???)
List specialCaches = new ArrayList<>();
Collection<String> cacheNames = cacheManager.getCacheNames();
if (CollectionUtil.isNotEmpty(cacheNames)) {
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
specialCaches.add(cache);
}
}
//下面是各特殊緩存
if (StringUtils.isNotBlank(specialCachesStr)) {
log.info("----配置特殊緩存開始----");
addSpecialCache(specialCaches, specialCachesStr);
log.info("----配置特殊緩存結束----");
}
//再把所有緩存放進manager
cacheManager.setCaches(specialCaches);
return cacheManager;
}
/**
* 循環建立特殊緩存
*
* @param specialCaches 自定義緩存集合
* @param specialCachesStr 特殊緩存參數字元串
*/
private void addSpecialCache(List specialCaches, String specialCachesStr) {
String[] spCaches = specialCachesStr.split(",");
if (ArrayUtils.isNotEmpty(spCaches)) {
for (String spCache : spCaches) {
String[] cacheParam = spCache.split("[+]");
if (cacheParam.length == 3) {
String cacheName = cacheParam[0].trim();
Long cacheMaxSize = Long.parseLong(cacheParam[1].trim());
Long cacheDuration = Long.parseLong(cacheParam[2].trim());
//初始化
@NonNull
Caffeine<Object, Object> dictLoadCache = Caffeine.newBuilder();
dictLoadCache.removalListener((Object key, Object value, RemovalCause cause) -> {
//這裡可以增加緩存消失的通知等等
log.info("Key %s was removed value=%s%n", key, value);
});
//逾時時間設定
if (cacheDuration != null && cacheDuration > 0) {
dictLoadCache.expireAfterWrite(cacheDuration, TimeUnit.SECONDS);
}
//這裡是設定數量,驅逐就根據數量,也可以用大小限定緩存,逐出政策有三種,大小、權重、時間
if (cacheMaxSize != null) {
dictLoadCache.maximumSize(cacheMaxSize);
}
log.info("----初始化特異緩存:{},最大值{},時長{}", cacheName, cacheMaxSize, cacheDuration);
CaffeineCache cafCache = new CaffeineCache(cacheName, dictLoadCache.build(key -> cacheName));
specialCaches.add(cafCache);
}
}
}
}
}
這裡其實就是依賴配置3個關鍵參數,建立多個特殊緩存,1個類似預設的不設定長度、不設定逾時時間的。
三、配置檔案
#caffeine本地緩存配置
spring.cache.type=caffeine
#配置設定最大數量、逾時時間,不配置預設不限制
#spring.cache.caffeine.spec=maximumSize=100000,expireAfterAccess=60s
spring.cache.caffeine.spec=maximumSize=100000
#本地特殊緩存設定
spring.cache.caffeine.special.caches=dict + 10000 + -1,select + 1000 + 20
caches就是設定多個緩存的配置,都設定3個參數,至于政策隻能寫一樣的了,如果說想不一樣,其實也可以,那就建議用json字元串,這樣好了解,也好用。
這裡要說明一下就是啟動類一定要開啟緩存,有@EnableCaching注解。不然一切都是浮雲。
到此就設定完成了,順便附上我的一個簡單的工具類吧。
四、簡單工具類
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
/**
* 本地緩存工具類
*
* @author zhengwen
**/
@Slf4j
public class CaffeineLocalCacheUtil {
/**
* 利用緩存記數
*
* @param countSign 計數辨別
* @param cacheManager 緩存管理對象
*/
public synchronized static void addCount(String countSign, CacheManager cacheManager) {
if (StringUtils.isNotBlank(countSign) && cacheManager != null) {
Cache countSignCache = cacheManager.getCache(countSign);
Cache.ValueWrapper cacheVal = countSignCache.get("num");
Long count = 1L;
if (cacheVal != null) {
count = (Long) cacheVal.get();
count += 1;
}
countSignCache.put("num", count);
log.info("===={}緩存數量值:{}", countSign, count);
}
}
/**
* 讀取緩存數量
*
* @param cacheName 緩存名稱
* @param cacheManager 緩存管理
* @return 緩存數量
*/
public static Long getCount(String cacheName, CacheManager cacheManager) {
Long count = 0L;
if (StringUtils.isNotBlank(cacheName) && cacheManager != null) {
Cache countSignCache = cacheManager.getCache(cacheName);
Cache.ValueWrapper cacheVal = countSignCache.get("num");
if (cacheVal != null) {
count = (Long) cacheVal.get();
}
}
log.info("===={}緩存數量值:{}", cacheName, count);
return count;
}
/**
* 擷取緩存值
*
* @param cacheName 緩存名稱
* @param key 緩存key
* @param cacheManager 緩存管理
* @return 緩存值
*/
public static Cache.ValueWrapper getCacheValue(String cacheName, String key, CacheManager cacheManager) {
if (StringUtils.isBlank(cacheName) || StringUtils.isBlank(key) || cacheManager == null) {
return null;
}
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
log.info("----緩存:{}不存在", cacheName);
return null;
}
Cache.ValueWrapper cacheVal = cache.get(key);
if (cacheVal != null) {
return cacheVal;
}
return null;
}
/**
* 設定緩存值
*
* @param cacheName 緩存名稱
* @param key 緩存key
* @param cacheManager 緩存管理器
* @param cacheDataStr 緩存值字元串
*/
public static boolean setCacheValue(String cacheName, String key, CacheManager cacheManager, String cacheDataStr) {
if (StringUtils.isAnyBlank(cacheName, key, cacheDataStr) || cacheManager == null) {
log.info("----緩存參數錯誤,不做資料緩存");
return false;
} else {
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
log.info("----緩存:{}不存在", cacheName);
return false;
}
cache.put(key, cacheDataStr);
return true;
}
}
/**
* 緩存内容銷毀
*
* @param cacheName 緩存名稱
* @param key 緩存key
* @param cacheManager 緩存管理器
* @return 銷毀結果
*/
public static boolean destroyCacheValue(String cacheName, String key, CacheManager cacheManager) {
if (StringUtils.isAnyBlank(cacheName, key) || cacheManager == null) {
log.info("----緩存參數錯誤,不做資料緩存");
return false;
} else {
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
log.info("----緩存:{}不存在", cacheName);
return false;
}
cache.evictIfPresent(key);
return true;
}
}
}
五、我的使用場景
我這裡是用來緩存字典表資料,将緩存邏輯放在了一個service方法裡,項目啟動進行調用。
/**
* 啟動後自動運作入口
*
* @author zhengwen
**/
@Slf4j
@Component
@Lazy
public class StartAfterBootRunner implements ApplicationRunner {
@Resource
private SysDictService sysDictService;
@Override
public void run(ApplicationArguments args){
log.info("--啟動後自動運作--");
log.info("--緩存字典資料開始--");
Result<?> res = sysDictService.queryDictAndCache(null,null);
log.info("Caffeine本地緩存字典資料結果:{}", JSON.toJSONString(res));
log.info("--緩存字典資料結束--");
}
}
queryDictAndCache方法裡就是查詢資料庫标,然後用工具類緩存到指定的緩存裡,裡面有一點點涉密,就不貼出來給大家了。辛苦大家自己寫,哈哈。