基于Caffeine自定义接口实现本地缓存

一、简介

caffeine是基于java8,Guava开发的一款高性能的本地缓存,是现场安全的本地缓存,可以用于多个场景:
1.用于用户重复提交数据验证
2.缓存数据
3.解决之前缓存的限制大小,导致OOM
4.通过设置时间解决内存占用资源浪费
5.防止api接口重复调用风险--性能提升

二、实战

1.添加依赖

	<dependency>
		<groupId>com.github.ben-manes.caffeine</groupId>
		<artifactId>caffeine</artifactId>
		<version>2.9.2</version>
	</dependency>

2.定义自定义接口

package com.test.utils.caffeine;

import com.github.benmanes.caffeine.cache.stats.CacheStats;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;

/**
 * Caffeine封装接口
 */
public interface CaffeineCache<K, V> {

    /**
     * put
     *
     * @param key
     * @param value
     */
    void put(K key, V value);

    /**
     * get
     *
     * @param key
     * @return
     */
    V get(K key);

    /**
     * 判断是否包含K
     *
     * @param key
     * @return
     */
    boolean containsKey(K key);

    /**
     * 判断是否包含V
     *
     * @param value
     * @return
     */
    boolean containsValue(V value);

    /**
     * 移除某个K
     *
     * @param key
     */
    void remove(Object key);

    /**
     * 查询缓存命中,驱逐等数量
     *
     * @return
     */
    CacheStats cacheStats();

    /**
     * 清除全部(性能较慢,考虑场景使用)
     */
    void clear();

    /**
     * 转成MAP
     *
     * @return
     */
    ConcurrentMap<K, V> asMap();

    /**
     * 获取values
     *
     * @return
     */
    Collection<V> values();

    /**
     * 获取缓存大小
     *
     * @return
     */
    long size();

    /**
     * 主动回收已失效的缓存
     *
     * @return
     */
    void cleanUp();

    /**
     * 当缓存中有这个key就使用key对应的value值 如果没有就使用默认的value
     *
     * @return
     */
    V getOrDefault(K k, V v);

    /**
     * entrySet
     *
     * @return
     */
    Set<Map.Entry<K, V>> entrySet();
}

2.自定义接口实现类

package com.test.utils.caffeine;

import com.github.benmanes.caffeine.cache.*;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * Caffeine封装API实现类
 */
@Slf4j
public class CaffeineCacheLocal<K, V> implements CaffeineCache<K, V> {
    private final Cache<K, V> localCache;

    private RemovalListener<? super K, ? super V> removalListener;
    private long maximumSize = -1L;
    private long duration = -1L;
    private TimeUnit unit;

    public CaffeineCacheLocal() {
        localCache = initCache();
    }

    public CaffeineCacheLocal(RemovalListener<? super K, ? super V> removalListener, long maximumSize, long duration, TimeUnit unit) {
        if (removalListener != null) {
            this.removalListener = removalListener;
        }
        if (unit != null) {
            this.unit = unit;
        }
        this.duration = duration;
        this.maximumSize = maximumSize;
        this.localCache = initCache();
    }

    /**
     * 初始化
     *
     * @return
     */
    private Cache<K, V> initCache() {
        Caffeine<Object, Object> caffeine = Caffeine.newBuilder();
        //暂时未加入权重逻辑 所以maximumSize必须设定
        //若加入权重逻辑后,可以根据是否有权重判断处理
        if (this.maximumSize <= 0L) {
            throw new RuntimeException("maximumSize is must be set");
        }

        //key的最大size
        caffeine.maximumSize(this.maximumSize);

        //expireAfterWrite全局时间淘汰策略
        if (this.duration > 0L && this.unit != null) {
            caffeine.expireAfterWrite(this.duration, this.unit);
        }

        //开启淘汰监听
        if (this.removalListener != null) {
            caffeine.removalListener(this.removalListener);
        }

        return caffeine.build();
    }

    @Override
    public void put(K key, final V value) {
        localCache.put(key, value);
    }

    @Override
    public V get(K key) {
        if (Objects.nonNull(key)){
            return localCache.getIfPresent(key);
        }
        return null;
    }

    @Override
    public boolean containsKey(K key) {
        return asMap().containsKey(key);
    }

    @Override
    public boolean containsValue(V value) {
        return asMap().containsValue(value);
    }

    @Override
    public void remove(Object key) {
        localCache.invalidate(key);
    }

    @Override
    public CacheStats cacheStats() {
        return localCache.stats();
    }

    @Override
    public void clear() {
        localCache.invalidateAll();
    }

    @Override
    public ConcurrentMap<K, V> asMap() {
        return localCache.asMap();
    }

    @Override
    public Collection<V> values() {
        return asMap().values();
    }

    @Override
    public long size() {
        return localCache.estimatedSize();
    }

    @Override
    public void cleanUp() {
        localCache.cleanUp();
    }

    @Override
    public V getOrDefault(K k, V defaultValue) {
        V v;
        return ((v = get(k)) != null) ? v : defaultValue;
    }

    @Override
    public Set<Map.Entry<K, V>> entrySet() {
        return asMap().entrySet();
    }

    public static Builder<Object, Object> newBuilder() {
        return new Builder<>();
    }

    public static class Builder<K1, V1> {
        private RemovalListener<? super K1, ? super V1> removalListener;
        private long maximumSize;
        private long duration;
        private TimeUnit unit;

        public Builder<K1, V1> removalListener(RemovalListener removalListener) {
            this.removalListener = removalListener;
            return this;
        }

        public Builder<K1, V1> maximumSize(long maximumSize) {
            this.maximumSize = maximumSize;
            return this;
        }

        public Builder<K1, V1> expireAfterWrite(long duration, TimeUnit unit) {
            this.duration = duration;
            this.unit = unit;
            return this;
        }

        public <K extends K1, V extends V1> CaffeineCache<K, V> build() {
            return new CaffeineCacheLocal<>(removalListener, maximumSize, duration, unit);
        }
    }
}

三、测试结果

public void expireAfterWriteTest() {
        CaffeineCache<Long, Set<String>> caffeineCache = CaffeineCacheLocal.newBuilder()
                                                                           .maximumSize(100)
                                                                           .expireAfterWrite(5, TimeUnit.SECONDS)
                                                                           .removalListener(new RemovalListener<Long, Set<String>>() {
                                                                               @Override
                                                                               public void onRemoval(@Nullable Long key, @Nullable Set<String> value, @NonNull RemovalCause removalCause) {
                                                                                   System.out.println("移除了key:" + key + "  value:" + value + " cause:" + removalCause);
                                                                               }
                                                                           })
                                                                           .build();
        caffeineCache.put(1L, Sets.newHashSet("1", "2"));
        caffeineCache.put(2L, Sets.newHashSet("3", "4"));
        System.out.println(caffeineCache.get(2L));
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(caffeineCache.get(2L));
    }

原文链接:https://blog.csdn.net/weixin_39382426/article/details/131392031

posted @   牛奶配苦瓜  阅读(76)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示