疯狂的狮子Li
2022-08-13 89c1e4f91de2a527ec825c76b0de5dd2fd42cc59
update 重写 spring-cache 实现 更人性化的操作 支持注解指定ttl等一些参数
已添加2个文件
已修改4个文件
306 ■■■■ 文件已修改
ruoyi-admin/src/main/resources/application.yml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application.yml
@@ -233,21 +233,6 @@
  # çº¿ç¨‹æ± ç»´æŠ¤çº¿ç¨‹æ‰€å…è®¸çš„空闲时间
  keepAliveSeconds: 300
--- # redisson ç¼“存配置
redisson:
  cacheGroup:
    # ç”¨ä¾‹: @Cacheable(cacheNames="groupId", key="#XXX") æ–¹å¯ä½¿ç”¨ç¼“存组配置
    - groupId: redissonCacheMap
      # ç»„过期时间(脚本监控)
      ttl: 60000
      # ç»„最大空闲时间(脚本监控)
      maxIdleTime: 60000
      # ç»„最大长度
      maxSize: 0
    - groupId: testCache
      ttl: 1000
      maxIdleTime: 500
--- # åˆ†å¸ƒå¼é” lock4j å…¨å±€é…ç½®
lock4j:
  # èŽ·å–åˆ†å¸ƒå¼é”è¶…æ—¶æ—¶é—´ï¼Œé»˜è®¤ä¸º 3000 æ¯«ç§’
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package com.ruoyi.common.constant;
/**
 * ç¼“存组名称常量
 * <p>
 * key æ ¼å¼ä¸º cacheNames#ttl#maxIdleTime#maxSize
 * <p>
 * ttl è¿‡æœŸæ—¶é—´ å¦‚果设置为0则不过期 é»˜è®¤ä¸º0
 * maxIdleTime æœ€å¤§ç©ºé—²æ—¶é—´ æ ¹æ®LRU算法清理空闲数据 å¦‚果设置为0则不检测 é»˜è®¤ä¸º0
 * maxSize ç»„最大长度 æ ¹æ®LRU算法清理溢出数据 å¦‚果设置为0则无限长 é»˜è®¤ä¸º0
 * <p>
 * ä¾‹å­: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
 *
 * @author Lion Li
 */
public interface CacheNames {
    /**
     * æ¼”示案例
     */
    String DEMO_CACHE = "demo:cache#60s#10m#20";
}
ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java
@@ -1,5 +1,6 @@
package com.ruoyi.demo.controller;
import com.ruoyi.common.constant.CacheNames;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.redis.RedisUtils;
import lombok.RequiredArgsConstructor;
@@ -18,7 +19,7 @@
 * @author Lion Li
 */
// ç±»çº§åˆ« ç¼“存统一配置
//@CacheConfig(cacheNames = "redissonCacheMap")
//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
@RequiredArgsConstructor
@RestController
@RequestMapping("/demo/cache")
@@ -36,9 +37,9 @@
     * é‡ç‚¹è¯´æ˜Ž: ç¼“存注解严谨与其他筛选数据功能一起使用
     * ä¾‹å¦‚: æ•°æ®æƒé™æ³¨è§£ ä¼šé€ æˆ ç¼“存击穿 ä¸Ž æ•°æ®ä¸ä¸€è‡´é—®é¢˜
     * <p>
     * cacheNames ä¸ºé…ç½®æ–‡ä»¶å†… groupId
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @Cacheable(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
    @Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
    @GetMapping("/test1")
    public R<String> test1(String key, String value) {
        return R.ok("操作成功", value);
@@ -48,11 +49,11 @@
     * æµ‹è¯• @CachePut
     * <p>
     * åŠ äº†@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
     * å®ƒã€Œé€šå¸¸ç”¨åœ¨æ–°å¢žæ–¹æ³•上」
     * å®ƒã€Œé€šå¸¸ç”¨åœ¨æ–°å¢žæˆ–者实时更新方法上」
     * <p>
     * cacheNames ä¸º é…ç½®æ–‡ä»¶å†… groupId
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @CachePut(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
    @CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
    @GetMapping("/test2")
    public R<String> test2(String key, String value) {
        return R.ok("操作成功", value);
@@ -62,11 +63,11 @@
     * æµ‹è¯• @CacheEvict
     * <p>
     * ä½¿ç”¨äº†CacheEvict注解的方法,会清空指定缓存
     * ã€Œä¸€èˆ¬ç”¨åœ¨æ›´æ–°æˆ–者删除的方法上」
     * ã€Œä¸€èˆ¬ç”¨åœ¨åˆ é™¤çš„æ–¹æ³•上」
     * <p>
     * cacheNames ä¸º é…ç½®æ–‡ä»¶å†… groupId
     * cacheNames å‘½åè§„则 æŸ¥çœ‹ {@link CacheNames} æ³¨é‡Š æ”¯æŒå¤šå‚æ•°
     */
    @CacheEvict(cacheNames = "redissonCacheMap", key = "#key", condition = "#key != null")
    @CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
    @GetMapping("/test3")
    public R<String> test3(String key, String value) {
        return R.ok("操作成功", value);
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java
@@ -4,11 +4,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.framework.config.properties.RedissonProperties;
import com.ruoyi.framework.handler.KeyPrefixHandler;
import com.ruoyi.framework.manager.PlusSpringCacheManager;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonSpringCacheManager;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -17,10 +15,6 @@
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * redis配置
@@ -80,18 +74,11 @@
    }
    /**
     * æ•´åˆspring-cache
     * è‡ªå®šä¹‰ç¼“存管理器 æ•´åˆspring-cache
     */
    @Bean
    public CacheManager cacheManager(RedissonClient redissonClient) {
        List<RedissonProperties.CacheGroup> cacheGroup = redissonProperties.getCacheGroup();
        Map<String, CacheConfig> config = new HashMap<>();
        for (RedissonProperties.CacheGroup group : cacheGroup) {
            CacheConfig cacheConfig = new CacheConfig(group.getTtl(), group.getMaxIdleTime());
            cacheConfig.setMaxSize(group.getMaxSize());
            config.put(group.getGroupId(), cacheConfig);
        }
        return new RedissonSpringCacheManager(redissonClient, config, new JsonJacksonCodec(objectMapper));
    public CacheManager cacheManager() {
        return new PlusSpringCacheManager();
    }
    /**
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java
@@ -7,8 +7,6 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * Redisson é…ç½®å±žæ€§
 *
@@ -18,12 +16,12 @@
@Component
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
    /**
     * redis缓存key前缀
     */
    private String keyPrefix;
    /**
     * çº¿ç¨‹æ± æ•°é‡,默认值 = å½“前处理核数量 * 2
     */
@@ -43,11 +41,6 @@
     * é›†ç¾¤æœåŠ¡é…ç½®
     */
    private ClusterServersConfig clusterServersConfig;
    /**
     * ç¼“存组
     */
    private List<CacheGroup> cacheGroup;
    @Data
    @NoArgsConstructor
@@ -138,32 +131,6 @@
         * è®¢é˜…模式
         */
        private SubscriptionMode subscriptionMode;
    }
    @Data
    @NoArgsConstructor
    public static class CacheGroup {
        /**
         * ç»„id
         */
        private String groupId;
        /**
         * ç»„过期时间
         */
        private long ttl;
        /**
         * ç»„最大空闲时间
         */
        private long maxIdleTime;
        /**
         * ç»„最大长度
         */
        private int maxSize;
    }
ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
/**
 * Copyright (c) 2013-2021 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruoyi.framework.manager;
import com.ruoyi.common.utils.redis.RedisUtils;
import org.redisson.api.RMap;
import org.redisson.api.RMapCache;
import org.redisson.spring.cache.CacheConfig;
import org.redisson.spring.cache.RedissonCache;
import org.springframework.boot.convert.DurationStyle;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
 * A {@link org.springframework.cache.CacheManager} implementation
 * backed by Redisson instance.
 * <p>
 * ä¿®æ”¹ RedissonSpringCacheManager æºç 
 * é‡å†™ cacheName å¤„理方法 æ”¯æŒå¤šå‚æ•°
 *
 * @author Nikita Koksharov
 *
 */
@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
    private boolean dynamic = true;
    private boolean allowNullValues = true;
    private boolean transactionAware = true;
    Map<String, CacheConfig> configMap = new ConcurrentHashMap<>();
    ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
    /**
     * Creates CacheManager supplied by Redisson instance
     */
    public PlusSpringCacheManager() {
    }
    /**
     * Defines possibility of storing {@code null} values.
     * <p>
     * Default is <code>true</code>
     *
     * @param allowNullValues stores if <code>true</code>
     */
    public void setAllowNullValues(boolean allowNullValues) {
        this.allowNullValues = allowNullValues;
    }
    /**
     * Defines if cache aware of Spring-managed transactions.
     * If {@code true} put/evict operations are executed only for successful transaction in after-commit phase.
     * <p>
     * Default is <code>false</code>
     *
     * @param transactionAware cache is transaction aware if <code>true</code>
     */
    public void setTransactionAware(boolean transactionAware) {
        this.transactionAware = transactionAware;
    }
    /**
     * Defines 'fixed' cache names.
     * A new cache instance will not be created in dynamic for non-defined names.
     * <p>
     * `null` parameter setups dynamic mode
     *
     * @param names of caches
     */
    public void setCacheNames(Collection<String> names) {
        if (names != null) {
            for (String name : names) {
                getCache(name);
            }
            dynamic = false;
        } else {
            dynamic = true;
        }
    }
    /**
     * Set cache config mapped by cache name
     *
     * @param config object
     */
    public void setConfig(Map<String, ? extends CacheConfig> config) {
        this.configMap = (Map<String, CacheConfig>) config;
    }
    protected CacheConfig createDefaultConfig() {
        return new CacheConfig();
    }
    @Override
    public Cache getCache(String name) {
        Cache cache = instanceMap.get(name);
        if (cache != null) {
            return cache;
        }
        if (!dynamic) {
            return cache;
        }
        CacheConfig config = configMap.get(name);
        if (config == null) {
            config = createDefaultConfig();
            configMap.put(name, config);
        }
        // é‡å†™ cacheName æ”¯æŒå¤šå‚æ•°
        String[] array = StringUtils.delimitedListToStringArray(name, "#");
        name = array[0];
        if (array.length > 1) {
            config.setTTL(DurationStyle.detectAndParse(array[1]).toMillis());
        }
        if (array.length > 2) {
            config.setMaxIdleTime(DurationStyle.detectAndParse(array[2]).toMillis());
        }
        if (array.length > 3) {
            config.setMaxSize(Integer.parseInt(array[3]));
        }
        if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) {
            return createMap(name, config);
        }
        return createMapCache(name, config);
    }
    private Cache createMap(String name, CacheConfig config) {
        RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
        Cache cache = new RedissonCache(map, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }
        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        }
        return cache;
    }
    private Cache createMapCache(String name, CacheConfig config) {
        RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
        Cache cache = new RedissonCache(map, config, allowNullValues);
        if (transactionAware) {
            cache = new TransactionAwareCacheDecorator(cache);
        }
        Cache oldCache = instanceMap.putIfAbsent(name, cache);
        if (oldCache != null) {
            cache = oldCache;
        } else {
            map.setMaxSize(config.getMaxSize());
        }
        return cache;
    }
    @Override
    public Collection<String> getCacheNames() {
        return Collections.unmodifiableSet(configMap.keySet());
    }
}