当前位置:   article > 正文

spring cache实现二级缓存(Caffeine+Redis)_spring cache 二级缓存

spring cache 二级缓存
为什么需要二级缓存

redis会需要网络通信,本地缓存不需要,如果采用二级缓存会提高效率

本地缓存也可以叫做应用缓存,网络开销很小

如果采用用redis,会涉及到请求,有网络上的开销

本地缓存常用选型

本人现在没有过多研究选型相关,参考链接如下:
Java本地缓存技术选型(Guava Cache、Caffeine、Encache)
本地缓存选型(Guava/Caffeine/Ohc)及性能对比

我本次使用Caffeine

springboot中集成spring cache,已经有了多种缓存方式的实现,例如Redis、Caffeine、JCache、EhCache等
在这里插入图片描述

使用缓存流程
  1. 从一级缓存读出数据,如果存在则直接进行业务
  2. 不存在则读取二级缓存,如果存在则更新一级缓存
  3. 不存在则从数据库读,然后依次更新二级缓存、一级缓存,然后业务处理
spring cache

springcache本身并没有提供缓存的实现,但是他提供了一套的接口、代码规范、配置、注解等,这样很容易就可以整合各种缓存,springcache主要包含了两个核心的接口来统一管理缓存相关的实现。分别是:

  • org.springframework.cache.Cache 定义了通用缓存操作的接口
  • org.springframework.cache.CacheManager 是一个spring的缓存管理器,主要用于管理不同的缓存

引入pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
Caffeine

我们使用Caffeine来作为本地的缓存,只需要引入

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4
实现步骤
  • 定义缓存的配置文件
  • 实现Cache 接口
  • 实现CacheManager接口
  • 增加spring boot配置类
具体实现
定义缓存的配置文件

定义properties配置属性类,主要用于以配置文件进行配置。

@ConfigurationProperties(prefix = "model.cache.multi")
public class RedisCaffeineCacheProperties {

    /**
     * 静态缓存名称
     */
    private Set<String> cacheNames = new HashSet<>();

    /**
     * 一级缓存名称
     */
    private Set<String> firstCacheNames = new HashSet<>();


    /**
     * 是否存储空值,默认true
     */
    private boolean storeNullValues = true;

    /**
     * 缓存redis key的前缀
     */
    private String redisKeyPrefix = "";

    private Redis redis = new Redis();

    private Caffeine caffeine = new Caffeine();


    public class Redis {

        //设置全局过期时间,默认8天
        private long defaultExpiration = 60 * 60 * 24 * 8;

        //缓存更新时通知其他节点的topic名称
        private String topic = "cache:redis:caffeine:topic";

        //每个cacheName的过期时间,单位毫秒,优先级比defaultExpiration高
        private Map<String, Long> expires = new HashMap<>();

        public Map<String, Long> getExpires() {
            return expires;
        }

        public void setExpires(Map<String, Long> expires) {
            this.expires = expires;
        }

        public long getDefaultExpiration() {
            return defaultExpiration;
        }

        public void setDefaultExpiration(long defaultExpiration) {
            this.defaultExpiration = defaultExpiration;
        }

        public String getTopic() {
            return topic;
        }

        public void setTopic(String topic) {
            this.topic = topic;
        }
    }

    public class Caffeine {

        //访问后过期时间,单位毫秒
        private long expireAfterAccess;

        //写入后过期时间,单位毫秒
        private long expireAfterWrite;

        //写入后刷新时间,单位毫秒
        private long refreshAfterWrite;

        //设置最大缓存对象个数,超过此数量时之前放入的缓存将失效
        private int maximumSize;

        //初始化大小
        private int initialCapacity;

        //每个cacheName的写入后过期时间(expireAfterWrite),找不到就用上面定义的配置。单位毫秒
        private Map<String, Long> expires = new HashMap<>();

        public long getExpireAfterAccess() {
            return expireAfterAccess;
        }

        public void setExpireAfterAccess(long expireAfterAccess) {
            this.expireAfterAccess = expireAfterAccess;
        }

        public long getExpireAfterWrite() {
            return expireAfterWrite;
        }

        public void setExpireAfterWrite(long expireAfterWrite) {
            this.expireAfterWrite = expireAfterWrite;
        }

        public long getRefreshAfterWrite() {
            return refreshAfterWrite;
        }

        public void setRefreshAfterWrite(long refreshAfterWrite) {
            this.refreshAfterWrite = refreshAfterWrite;
        }

        public int getMaximumSize() {
            return maximumSize;
        }

        public void setMaximumSize(int maximumSize) {
            this.maximumSize = maximumSize;
        }

        public int getInitialCapacity() {
            return initialCapacity;
        }

        public void setInitialCapacity(int initialCapacity) {
            this.initialCapacity = initialCapacity;
        }

        public Map<String, Long> getExpires() {
            return expires;
        }

        public void setExpires(Map<String, Long> expires) {
            this.expires = expires;
        }
    }

    public boolean isStoreNullValues() {
        return storeNullValues;
    }

    public void setStoreNullValues(boolean storeNullValues) {
        this.storeNullValues = storeNullValues;
    }

    public String getRedisKeyPrefix() {
        return redisKeyPrefix;
    }

    public void setRedisKeyPrefix(String redisKeyPrefix) {
        this.redisKeyPrefix = redisKeyPrefix;
    }

    public Set<String> getCacheNames() {
        return cacheNames;
    }

    public void setCacheNames(Set<String> cacheNames) {
        this.cacheNames = cacheNames;
    }

    public Redis getRedis() {
        return redis;
    }

    public void setRedis(Redis redis) {
        this.redis = redis;
    }

    public Caffeine getCaffeine() {
        return caffeine;
    }

    public void setCaffeine(Caffeine caffeine) {
        this.caffeine = caffeine;
    }

    public Set<String> getFirstCacheNames() {
        return firstCacheNames;
    }

    public void setFirstCacheNames(Set<String> firstCacheNames) {
        this.firstCacheNames = firstCacheNames;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
实现Cache 接口

对多级缓存的具体操作实现,例如对缓存的get、put、evict操作

public class RedisCaffeineCache extends AbstractValueAdaptingCache {

    private static final Logger log = LoggerFactory.getLogger(RedisCaffeineCache.class);

    private RedisTemplate<String, Object> redisTemplate;


    /**
     * 多级缓存的唯一标识
     */
    private String uniqueKey = UUID.randomUUID().toString();

    /**
     * 多级缓存的名称
     */
    private String name;

    /**
     * redis key前缀
     */
    private String cachePrefix;

    /**
     * redis key 的过期时间
     */
    private Map<String, Long> expires;

    /**
     * redis 全局过期时间
     */
    private long defaultExpiration = 0;

    /**
     * 定义一个全局的公平锁
     */
    private static ReentrantLock fairLock = new ReentrantLock(true);


    /**
     * 是否开启一级缓存
     */
    private boolean firstCache;

    /**
     * 一级缓存
     */
    private Cache<Object, Object> caffeineCache;

    /**
     * redis消息队列的topic
     */
    private String topic = "cache:redis:caffeine:topic";


    public RedisCaffeineCache(String name, RedisTemplate<String, Object> redisTemplate, Cache<Object, Object> caffeineCache,RedisCaffeineCacheProperties redisCaffeineCacheProperties) {
        super(redisCaffeineCacheProperties.isStoreNullValues());
        this.name = name;
        this.redisTemplate = redisTemplate;
        this.cachePrefix = redisCaffeineCacheProperties.getRedisKeyPrefix();
        this.expires = redisCaffeineCacheProperties.getRedis().getExpires();
        this.defaultExpiration = redisCaffeineCacheProperties.getRedis().getDefaultExpiration();
        this.topic = redisCaffeineCacheProperties.getRedis().getTopic();
        this.caffeineCache = caffeineCache;
        if (this.caffeineCache == null) {
            this.firstCache = false;
        }else{
            this.firstCache = true;
        }
    }


    /**
     * 执行底层查询操作
     * @param key
     * @return
     */
    @Override
    protected Object lookup(Object key) {
        //如果开启了一级缓存,则先从一级缓存中查询
        if(firstCache){
            Object value = caffeineCache.getIfPresent(key);
            if(ObjectUtils.isNotEmpty(value)) {
                log.info("从caffeine中获取数据:{}",value);
                return value;
            }
        }
        String redisKey = getKey(key);
        Object value = redisTemplate.opsForValue().get(redisKey);
        if(ObjectUtils.isNotEmpty(value)) {
            log.info("从redis中获取数据:{}",value);
            caffeineCache.put(key, value);
        }
        return value;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public Object getNativeCache() {
        return this;
    }

    /**
     * get cache value from  RedisCaffeineCache
     * @param key
     * @param valueLoader
     * @param <T>
     * @return
     */
    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        Object value = lookup(key);
        if(ObjectUtils.isNotEmpty(value)) {
            return (T) value;
        }
        fairLock.lock();
        try {
            value = lookup(key);
            if(ObjectUtils.isNotEmpty(value)) {
                return (T) value;
            }
            value = valueLoader.call();
            Object storeValue = toStoreValue(value);
            put(key, storeValue);
            return (T) value;

        } catch (Exception e) {
            throw  new ValueRetrievalException(key, valueLoader, e);
        }finally {
            fairLock.unlock();
        }
    }

    /**
     * put value into RedisCaffeineCache
     * @param key
     * @param value
     */
    @Override
    public void put(Object key, Object value) {
        if (!super.isAllowNullValues() && ObjectUtils.isEmpty(value)) {
            this.evict(key);
            return;
        }

        long expire =  getExpire();
        if(expire > 0) {
            redisTemplate.opsForValue().set(getKey(key), toStoreValue(value), expire, TimeUnit.MILLISECONDS);
        }else {
            redisTemplate.opsForValue().set(getKey(key), toStoreValue(value));
        }

        if (firstCache) {
            //发送消息到消息队列
            push(new CacheMessage(name,key,uniqueKey));
            caffeineCache.put(key, value);
        }
    }

    /**
     * 如果不能存在put,存在返回oldValue
     * @param key
     * @param value
     * @return
     */
    @Override
    public ValueWrapper putIfAbsent(Object key, Object value) {
        String cacheKey = getKey(key);
        Object oldValue = null;
        synchronized (key) {
            oldValue = redisTemplate.opsForValue().get(cacheKey);
            if (ObjectUtils.isEmpty(oldValue)) {
                long expire = getExpire();
                if (expire > 0) {
                    redisTemplate.opsForValue().set(cacheKey, toStoreValue(value), expire, TimeUnit.MILLISECONDS);
                }else {
                    redisTemplate.opsForValue().set(cacheKey, toStoreValue(value));
                }
                if (firstCache) {
                    push(new CacheMessage(name,key,uniqueKey));
                    caffeineCache.put(key, value);
                }
            }
        }
        return toValueWrapper(oldValue);
    }

    @Override
    public void evict(Object key) {
        //删除redis中的缓存
        redisTemplate.delete(getKey(key));
        //删除一级缓存
        if (firstCache) {
            push(new CacheMessage(name,key,uniqueKey));
            caffeineCache.invalidate(key);
        }
    }

    @Override
    public void clear() {
        Set<String> scan = RedisHelper.scan(redisTemplate, this.name.concat(":*"));
        redisTemplate.delete(scan);

        if (firstCache) {
            push(new CacheMessage(name,null,uniqueKey));
            caffeineCache.invalidateAll();
        }
    }
    /**
     * 重写fromStoreValue方法 ,避免获取NULLValue报错的问题
     * @param storeValue
     * @return
     */
    @Nullable
    @Override
    protected Object fromStoreValue(Object storeValue) {
        if (super.isAllowNullValues() && (storeValue == NullValue.INSTANCE || storeValue instanceof NullValue)) {
            return null;
        }
        return storeValue;
    }

    /**
     * 获取redisKey缓存名称
     *
     * @return
     */
    private String getKey(Object key) {
        return name.concat(":").concat(StringUtils.isEmpty(cachePrefix) ? key.toString() : cachePrefix.concat(":").concat(key.toString()));
    }

    /**
     * 获取redis 缓存过期时间
     * @return
     */
    private long getExpire() {
        Long cacheNameExpire = expires.get(name);
        return cacheNameExpire == null ? defaultExpiration : cacheNameExpire;
    }

    public Cache<Object, Object> getLocalCache() {
        return caffeineCache;
    }

    /**
     * 通知其他缓存删除一级缓存
     * @param message
     */
    private void push(CacheMessage message) {
        redisTemplate.convertAndSend(topic, message);
    }

    /**
     * 清除多级缓存值
     * @param key
     */
    public void clearLocal(Object key) {
        if (!firstCache) {
            return;
        }
        if (key == null) {
            caffeineCache.invalidateAll();
        } else {
            caffeineCache.invalidate(key);
        }
    }

    public String getUniqueKey(){
       return this.uniqueKey;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274

其中的push方法主要是通知其他缓存删除特定的一级缓存

CacheMessage实现如下

public class CacheMessage implements Serializable {

    private static final long serialVersionUID = -3742394520710710440L;

    /**
     * 多级缓存的名称
     */
    private String name;

    /**
     * 一级缓存的key
     */
    private Object key;

    /**
     * 多级缓存的唯一标识
     */
    private String redisCaffeineCacheUniqueKey;

    public CacheMessage() {
    }

    public CacheMessage(String name, Object key, String redisCaffeineCacheUniqueKey) {
        this.name = name;
        this.key = key;
        this.redisCaffeineCacheUniqueKey = redisCaffeineCacheUniqueKey;
    }

    public CacheMessage(String name, Object key) {
        this.name = name;
        this.key = key;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getKey() {
        return key;
    }

    public void setKey(Object key) {
        this.key = key;
    }

    public String getRedisCaffeineCacheUniqueKey() {
        return redisCaffeineCacheUniqueKey;
    }

    public void setRedisCaffeineCacheUniqueKey(String redisCaffeineCacheUniqueKey) {
        this.redisCaffeineCacheUniqueKey = redisCaffeineCacheUniqueKey;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

当给redis发送消息后,redis接收到消息会做处理,需要重写redis的消息监听器

CacheMessageListener实现如下

public class CacheMessageListener implements MessageListener {

    private static final Logger log = LoggerFactory.getLogger(CacheMessageListener.class);

    private RedisTemplate<String, Object> redisTemplate;

    private RedisCaffeineCacheManager redisCaffeineCacheManager;

    public CacheMessageListener(RedisTemplate<String, Object> redisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) {
        super();
        this.redisTemplate = redisTemplate;
        this.redisCaffeineCacheManager = redisCaffeineCacheManager;
    }


    @Override
    public void onMessage(Message message, byte[] pattern) {
        //处理收到的消息,去清除其他多级缓存的一级缓存
        CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
        log.debug("receive a redis topic message, clear local cache, the cacheName is {}, the key is {},the uniqueKey is {}", cacheMessage.getName(), cacheMessage.getKey(), cacheMessage.getRedisCaffeineCacheUniqueKey());
        redisCaffeineCacheManager.clearLocal(cacheMessage.getName(), cacheMessage.getKey(), cacheMessage.getRedisCaffeineCacheUniqueKey());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
实现CacheManager接口

主要是对多级缓存的管理,核心方法就是getCache() 。

public class RedisCaffeineCacheManager implements CacheManager {

    /**
     * key: cacheName value: redisCaffeineCache
     */
    private ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();

    private RedisTemplate<String, Object> stringKeyRedisTemplate;

    private RedisCaffeineCacheProperties redisCaffeineCacheProperties;

    /**
     * 是否根据cacheName动态生成
     */
    private boolean dynamic = true;

    /**
     * 不动态根据cacheName创建Cache的实现时,自定义设置的缓存名
     */
    private Set<String> cacheNames;

    /**
     * 一级缓存名集合
     */
    private Set<String> firstCacheNames;

    /**
     * 一级缓存的过期时间
     */
    private Map<String, Long> expires;

    public RedisCaffeineCacheManager(RedisTemplate<String, Object> redisTemplate, RedisCaffeineCacheProperties redisCaffeineCacheProperties) {
        super();
        this.stringKeyRedisTemplate = redisTemplate;
        this.redisCaffeineCacheProperties = redisCaffeineCacheProperties;
        this.cacheNames = redisCaffeineCacheProperties.getCacheNames();
        this.firstCacheNames = redisCaffeineCacheProperties.getFirstCacheNames();
        this.expires = redisCaffeineCacheProperties.getCaffeine().getExpires();

    }


    @Override
    public Cache getCache(String name) {
        Cache cache = cacheMap.get(name);
        if (cache != null) {
            return cache;
        }
        //如果不动态根据cacheName创建Cache的实现,且没有配置静态缓存  则返回空
        if (!dynamic && cacheNames.isEmpty()) {
            return null;
        }
        //如果开启一级缓存
        if (firstCacheNames.contains(name)) {
            //开启一级缓存
            cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(name), redisCaffeineCacheProperties);
        }else {
            // 只开启二级缓存 即只开启redis缓存
            cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, null, redisCaffeineCacheProperties);
        }
        Cache oldCache = cacheMap.putIfAbsent(name, cache);
        return oldCache == null ? cache : oldCache;
    }

    /**
     * 构建一个caffeineCache 即构建一个一级缓存
     * @param name
     * @return
     */
    private com.github.benmanes.caffeine.cache.Cache caffeineCache(String name) {
        Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder();
        if(redisCaffeineCacheProperties.getCaffeine().getExpireAfterAccess() > 0) {
            caffeineBuilder.expireAfterAccess(redisCaffeineCacheProperties.getCaffeine().getExpireAfterAccess(), TimeUnit.MILLISECONDS);
        }
        Long expire = expires.get(name);
        if (expire != null && expire > 0) {
            caffeineBuilder.expireAfterWrite(expire, TimeUnit.MILLISECONDS);
        }else if (redisCaffeineCacheProperties.getCaffeine().getExpireAfterWrite() > 0){
            caffeineBuilder.expireAfterWrite(redisCaffeineCacheProperties.getCaffeine().getExpireAfterAccess(), TimeUnit.MILLISECONDS);
        }
        if (redisCaffeineCacheProperties.getCaffeine().getRefreshAfterWrite() > 0) {
            caffeineBuilder.refreshAfterWrite(redisCaffeineCacheProperties.getCaffeine().getRefreshAfterWrite(), TimeUnit.MILLISECONDS);
        }
        if (redisCaffeineCacheProperties.getCaffeine().getInitialCapacity() > 0) {
            caffeineBuilder.initialCapacity(redisCaffeineCacheProperties.getCaffeine().getInitialCapacity());
        }
        if (redisCaffeineCacheProperties.getCaffeine().getMaximumSize() > 0) {
            caffeineBuilder.maximumSize(redisCaffeineCacheProperties.getCaffeine().getMaximumSize());
        }

        return caffeineBuilder.build();
    }

    @Override
    public Collection<String> getCacheNames() {
        return this.cacheNames;
    }

    /**
     * 清除 多级缓存中的某一级缓存
     * @param name
     * @param key
     * @param redisCaffeineCacheUniqueKey
     */
    public void clearLocal(String name, Object key,String redisCaffeineCacheUniqueKey) {
        Cache cache = cacheMap.get(name);
        if(cache == null) {
            return ;
        }

        RedisCaffeineCache redisCaffeineCache = (RedisCaffeineCache) cache;
        //判断是否是自己发送的消息 如果是自己发送的就不需要清楚缓存了
        if (StringUtils.isNoneEmpty(redisCaffeineCacheUniqueKey) && redisCaffeineCache.getUniqueKey().equals(redisCaffeineCacheUniqueKey)) {
            return;
        }
        redisCaffeineCache.clearLocal(key);

    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
增加spring boot配置类

只是我自己的一种实现,具体根据自己的项目来进行配置

@Configuration
@EnableConfigurationProperties(RedisCaffeineCacheProperties.class)
@EnableCaching
public class RedisCaffeineCacheAutoConfiguration {

    @Autowired
    private RedisCaffeineCacheProperties redisCaffeineCacheProperties;

    @Bean
    public RedisCaffeineCacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
        return new RedisCaffeineCacheManager(redisTemplate, redisCaffeineCacheProperties);
    }

    @Bean
    @ConditionalOnBean(name = {"redisTemplate", "cacheManager"})
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisTemplate<String, Object> redisTemplate,RedisCaffeineCacheManager redisCaffeineCacheManager) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisTemplate.getConnectionFactory());
        CacheMessageListener cacheMessageListener = new CacheMessageListener(redisTemplate, redisCaffeineCacheManager);
        redisMessageListenerContainer.addMessageListener(cacheMessageListener, new ChannelTopic(redisCaffeineCacheProperties.getRedis().getTopic()));
        return redisMessageListenerContainer;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

以上就是利用spring cache 实现多级缓存的核心,仅供参考。

总结

这是我第一次接触了解这种缓存实现,不足之处请见谅。

关于设置锁的地方,建议使用Redisson操作会更方便。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/740094
推荐阅读
相关标签
  

闽ICP备14008679号