Quellcode durchsuchen

update 重写 spring-cache 实现 更人性化的操作 支持注解指定ttl等一些参数

疯狂的狮子Li vor 2 Jahren
Ursprung
Commit
89c1e4f91d

+ 0 - 15
ruoyi-admin/src/main/resources/application.yml

@@ -233,21 +233,6 @@ thread-pool:
   # 线程池维护线程所允许的空闲时间
   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 毫秒

+ 23 - 0
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";
+
+}

+ 10 - 9
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 @@ import java.time.Duration;
  * @author Lion Li
  */
 // 类级别 缓存统一配置
-//@CacheConfig(cacheNames = "redissonCacheMap")
+//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/demo/cache")
@@ -36,9 +37,9 @@ public class RedisCacheController {
      * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用
      * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
      * <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 @@ public class RedisCacheController {
      * 测试 @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 @@ public class RedisCacheController {
      * 测试 @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);

+ 4 - 17
ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java

@@ -4,11 +4,9 @@ import cn.hutool.core.util.ObjectUtil;
 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;
@@ -18,10 +16,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 @@ public class RedisConfig extends CachingConfigurerSupport {
     }
 
     /**
-     * 整合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();
     }
 
     /**

+ 2 - 35
ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java

@@ -7,8 +7,6 @@ import org.redisson.config.SubscriptionMode;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
-
 /**
  * Redisson 配置属性
  *
@@ -18,12 +16,12 @@ import java.util.List;
 @Component
 @ConfigurationProperties(prefix = "redisson")
 public class RedissonProperties {
-    
+
     /**
      * redis缓存key前缀
      */
     private String keyPrefix;
-    
+
     /**
      * 线程池数量,默认值 = 当前处理核数量 * 2
      */
@@ -44,11 +42,6 @@ public class RedissonProperties {
      */
     private ClusterServersConfig clusterServersConfig;
 
-    /**
-     * 缓存组
-     */
-    private List<CacheGroup> cacheGroup;
-
     @Data
     @NoArgsConstructor
     public static class SingleServerConfig {
@@ -141,30 +134,4 @@ public class RedissonProperties {
 
     }
 
-    @Data
-    @NoArgsConstructor
-    public static class CacheGroup {
-
-        /**
-         * 组id
-         */
-        private String groupId;
-
-        /**
-         * 组过期时间
-         */
-        private long ttl;
-
-        /**
-         * 组最大空闲时间
-         */
-        private long maxIdleTime;
-
-        /**
-         * 组最大长度
-         */
-        private int maxSize;
-
-    }
-
 }

+ 191 - 0
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());
+    }
+
+
+}