guava本地缓存+函数式编程

举报
S-X-S 发表于 2025/01/09 11:08:24 2025/01/09
2.2k+ 0 0
【摘要】 1.引入依赖 1.sun-dependencies <guava.version>19.0</guava.version> <fastjson.version>1.2.83</fastjson.version> <!-- guava本地缓存 --> <dependency> ...

1.引入依赖

1.sun-dependencies

            <guava.version>19.0</guava.version>
           	<fastjson.version>1.2.83</fastjson.version>

            <!-- guava本地缓存 -->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>${guava.version}</version>
            </dependency>
            <!-- fastjson序列化 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>

2.sun-common-redis

        <!-- guava本地缓存 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>
        <!-- fastjson序列化 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

2.CacheUtil.java封装和使用

1.CacheUtil.java

package com.sunxiansheng.redis.util;

import com.alibaba.fastjson.JSON;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Description: Guava本地缓存工具类
 * @Author sun
 * @Create 2024/7/22 12:55
 * @Version 1.0
 */
@Component
@Slf4j
public class CacheUtil<K, V> {

    // 读取配置文件,支持动态刷新(需要配置Nacos)
    @Value("${guaca.cache.switch:false}")
    private Boolean cacheEnabled;

    @Value("${guaca.cache.maximumSize:5000}")
    private int maximumSize;

    @Value("${guaca.cache.expireAfterAccess:3}")
    private int expireAfterAccess;

    /**
     * 缓存key连接符
     */
    private static final String CACHE_KEY_SEPARATOR = "_";

    // 初始化一个Guava缓存
    private final Cache<String, String> localCache = CacheBuilder.newBuilder()
            .maximumSize(maximumSize)
            .expireAfterAccess(expireAfterAccess, TimeUnit.SECONDS)
            .build();

    /**
     * 使用本地缓存获取结果(批量)
     *
     * @param skuIdList id列表
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + id构成)
     * @param clazz 查询完结果后要反序列化的类型
     * @param function 不走缓存查询结果的逻辑
     * @return 查询结果的Map
     */
    public Map<K, V> getResult(List<K> skuIdList, String cachePrefix, Class<V> clazz, Function<List<K>, Map<K, V>> function) {
        // 判空
        if (CollectionUtils.isEmpty(skuIdList)) {
            return Collections.emptyMap();
        }

        Map<K, V> resultMap = new HashMap<>(16);

        // 如果缓存开关未开启,直接使用function查询
        if (Boolean.FALSE.equals(cacheEnabled)) {
            return function.apply(skuIdList);
        }

        // 记录未命中缓存的ID
        List<K> noCacheIdList = new LinkedList<>();

        // 查Guava本地缓存
        for (K skuId : skuIdList) {
            String cacheKey = generateCacheKey(cachePrefix, skuId);
            String content = localCache.getIfPresent(cacheKey);
            if (StringUtils.isNotBlank(content)) {
                V v = JSON.parseObject(content, clazz);
                resultMap.put(skuId, v);
            } else {
                noCacheIdList.add(skuId);
            }
        }

        // 记录未命中缓存的日志
        if (!CollectionUtils.isEmpty(noCacheIdList)) {
            log.debug("Cache miss for keys: {}", noCacheIdList);
        }

        // 如果所有ID都命中缓存,直接返回结果
        if (CollectionUtils.isEmpty(noCacheIdList)) {
            return resultMap;
        }

        // 使用function查询未命中缓存的ID
        Map<K, V> noCacheResultMap = function.apply(noCacheIdList);
        if (!CollectionUtils.isEmpty(noCacheResultMap)) {
            for (Map.Entry<K, V> entry : noCacheResultMap.entrySet()) {
                K skuId = entry.getKey();
                V content = entry.getValue();
                resultMap.put(skuId, content);
                String cacheKey = generateCacheKey(cachePrefix, skuId);
                localCache.put(cacheKey, JSON.toJSONString(content));
            }
        }

        return resultMap;
    }

    /**
     * 使用本地缓存获取单个结果
     *
     * @param key 缓存的键
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     * @param clazz 查询完结果后要反序列化的类型
     * @param function 不走缓存查询结果的逻辑
     * @return 查询结果
     * @throws ExecutionException
     */
    public V getSingleResult(K key, String cachePrefix, Class<V> clazz, Function<K, V> function) throws ExecutionException {
        String cacheKey = generateCacheKey(cachePrefix, key);
        String content = localCache.getIfPresent(cacheKey);
        if (StringUtils.isNotBlank(content)) {
            return JSON.parseObject(content, clazz);
        }

        V result = function.apply(key);
        if (result != null) {
            localCache.put(cacheKey, JSON.toJSONString(result));
        }

        return result;
    }

    /**
     * 清除单个缓存
     *
     * @param key 缓存的键
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     */
    public void invalidateSingleCache(K key, String cachePrefix) {
        String cacheKey = generateCacheKey(cachePrefix, key);
        localCache.invalidate(cacheKey);
        log.debug("Cache invalidated for key: {}", cacheKey);
    }

    /**
     * 清除多个缓存
     *
     * @param keys 缓存的键列表
     * @param cachePrefix 查询的前缀(缓存的key由前缀 + "_" + key构成)
     */
    public void invalidateMultipleCaches(List<K> keys, String cachePrefix) {
        List<String> cacheKeys = new ArrayList<>();
        for (K key : keys) {
            String cacheKey = generateCacheKey(cachePrefix, key);
            cacheKeys.add(cacheKey);
            log.debug("Cache invalidated for key: {}", cacheKey);
        }
        localCache.invalidateAll(cacheKeys);
    }

    /**
     * 清除所有缓存
     */
    public void invalidateAllCaches() {
        localCache.invalidateAll();
        log.debug("All caches invalidated.");
    }

    /**
     * 生成缓存键
     *
     * @param prefix 缓存前缀
     * @param id 缓存ID
     * @return 缓存键
     */
    private String generateCacheKey(String prefix, K id) {
        return prefix + CACHE_KEY_SEPARATOR + id;
    }
}

2.使用方式

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

@Service
public class CacheService {

    @Autowired
    private CacheUtil<String, String> cacheUtil;

    /**
     * 查询批量数据
     * @param ids 数据ID列表
     * @return 查询结果的Map
     */
    public Map<String, String> queryData(List<String> ids) {
        // 调用cacheUtil获取数据
        return cacheUtil.getResult(ids, "dataPrefix", String.class, this::loadFromDatabase);
    }

    /**
     * 查询单个数据
     * @param id 数据ID
     * @return 查询结果
     * @throws ExecutionException
     */
    public String querySingleData(String id) throws ExecutionException {
        // 调用cacheUtil获取单个数据
        return cacheUtil.getSingleResult(id, "dataPrefix", String.class, this::loadSingleFromDatabase);
    }

    /**
     * 更新数据并清除相关缓存
     * @param id 数据ID
     * @param newValue 新的数据值
     */
    public void updateData(String id, String newValue) {
        // 更新数据到数据库
        saveToDatabase(id, newValue);
        // 清除单个缓存
        cacheUtil.invalidateSingleCache(id, "dataPrefix");
    }

    /**
     * 批量更新数据并清除相关缓存
     * @param dataMap 数据Map
     */
    public void updateMultipleData(Map<String, String> dataMap) {
        // 更新数据到数据库
        saveMultipleToDatabase(dataMap);
        // 清除多个缓存
        cacheUtil.invalidateMultipleCaches(new ArrayList<>(dataMap.keySet()), "dataPrefix");
    }

    /**
     * 清除所有缓存
     */
    public void clearAllCaches() {
        cacheUtil.invalidateAllCaches();
    }

    // 模拟从数据库加载批量数据
    private Map<String, String> loadFromDatabase(List<String> ids) {
        // 实际实现时应从数据库或其他数据源加载数据
        // 这里简单模拟返回
        return Map.of("id1", "Value for id1", "id2", "Value for id2", "id3", "Value for id3");
    }

    // 模拟从数据库加载单个数据
    private String loadSingleFromDatabase(String id) {
        // 实际实现时应从数据库或其他数据源加载数据
        // 这里简单模拟返回
        return "Value for " + id;
    }

    // 模拟保存单个数据到数据库
    private void saveToDatabase(String id, String value) {
        // 实际实现时应保存到数据库或其他数据源
        // 这里简单模拟
        System.out.println("Saved " + value + " for " + id + " to database");
    }

    // 模拟保存多个数据到数据库
    private void saveMultipleToDatabase(Map<String, String> dataMap) {
        // 实际实现时应保存到数据库或其他数据源
        // 这里简单模拟
        dataMap.forEach((id, value) -> System.out.println("Saved " + value + " for " + id + " to database"));
    }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。