天天看點

Caffeine自定義使用配置多本地緩存(啟動加載字典資料)

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方法裡就是查詢資料庫标,然後用工具類緩存到指定的緩存裡,裡面有一點點涉密,就不貼出來給大家了。辛苦大家自己寫,哈哈。

繼續閱讀