From 89c1e4f91de2a527ec825c76b0de5dd2fd42cc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Sat, 13 Aug 2022 11:30:33 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E9=87=8D=E5=86=99=20spring-cache=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20=E6=9B=B4=E4=BA=BA=E6=80=A7=E5=8C=96?= =?UTF-8?q?=E7=9A=84=E6=93=8D=E4=BD=9C=20=E6=94=AF=E6=8C=81=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E6=8C=87=E5=AE=9Attl=E7=AD=89=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/application.yml | 15 -- .../com/ruoyi/common/constant/CacheNames.java | 23 +++ .../demo/controller/RedisCacheController.java | 19 +- .../ruoyi/framework/config/RedisConfig.java | 21 +- .../config/properties/RedissonProperties.java | 37 +--- .../manager/PlusSpringCacheManager.java | 191 ++++++++++++++++++ 6 files changed, 230 insertions(+), 76 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 4c54de53d..aef309ee3 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/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 毫秒 diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java new file mode 100644 index 000000000..892de005d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheNames.java @@ -0,0 +1,23 @@ +package com.ruoyi.common.constant; + +/** + * 缓存组名称常量 + *

+ * key 格式为 cacheNames#ttl#maxIdleTime#maxSize + *

+ * ttl 过期时间 如果设置为0则不过期 默认为0 + * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 + * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 + *

+ * 例子: 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"; + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java index 2312aab0e..7342cba98 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/RedisCacheController.java +++ b/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 { * 重点说明: 缓存注解严谨与其他筛选数据功能一起使用 * 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题 *

- * 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 test1(String key, String value) { return R.ok("操作成功", value); @@ -48,11 +49,11 @@ public class RedisCacheController { * 测试 @CachePut *

* 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用 - * 它「通常用在新增方法上」 + * 它「通常用在新增或者实时更新方法上」 *

- * 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 test2(String key, String value) { return R.ok("操作成功", value); @@ -62,11 +63,11 @@ public class RedisCacheController { * 测试 @CacheEvict *

* 使用了CacheEvict注解的方法,会清空指定缓存 - * 「一般用在更新或者删除的方法上」 + * 「一般用在删除的方法上」 *

- * 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 test3(String key, String value) { return R.ok("操作成功", value); diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java index c86952871..9a7938a4b 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java +++ b/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 cacheGroup = redissonProperties.getCacheGroup(); - Map 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(); } /** diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java index eab746c8b..b0bf285f3 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/RedissonProperties.java +++ b/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; - @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; - - } - } diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/PlusSpringCacheManager.java new file mode 100644 index 000000000..d8bfce7c5 --- /dev/null +++ b/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. + *

+ * 修改 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 configMap = new ConcurrentHashMap<>(); + ConcurrentMap instanceMap = new ConcurrentHashMap<>(); + + /** + * Creates CacheManager supplied by Redisson instance + */ + public PlusSpringCacheManager() { + } + + + /** + * Defines possibility of storing {@code null} values. + *

+ * Default is true + * + * @param allowNullValues stores if true + */ + 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. + *

+ * Default is false + * + * @param transactionAware cache is transaction aware if true + */ + 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. + *

+ * `null` parameter setups dynamic mode + * + * @param names of caches + */ + public void setCacheNames(Collection 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 config) { + this.configMap = (Map) 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 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 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 getCacheNames() { + return Collections.unmodifiableSet(configMap.keySet()); + } + + +}