From 8f66daa28d1ae3318d5d48f8334a24640c8b5ad6 Mon Sep 17 00:00:00 2001 From: hfdy2019 <5463852+hfdy2019@user.noreply.gitee.com> Date: Sat, 3 Aug 2024 06:13:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20caffeine=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E8=A3=85=E9=A5=B0=E5=99=A8=20getName=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E8=AE=BE=E7=BD=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: supreme --- .../common/redis/config/CacheConfig.java | 2 + .../AbstractPlusSpringCacheManager.java | 209 ++++++++++++++++++ .../redis/manager/CaffeineCacheDecorator.java | 2 +- .../redis/manager/PlusSpringCacheManager.java | 174 +-------------- .../common/tenant/config/TenantConfig.java | 1 - .../manager/TenantCaffeineCacheDecorator.java | 34 +++ .../manager/TenantSpringCacheManager.java | 26 ++- 7 files changed, 274 insertions(+), 174 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/AbstractPlusSpringCacheManager.java create mode 100644 ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantCaffeineCacheDecorator.java diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java index d57ba4e65..41a2bdc17 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/CacheConfig.java @@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.dromara.common.redis.manager.PlusSpringCacheManager; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @@ -38,6 +39,7 @@ public class CacheConfig { * 自定义缓存管理器 整合spring-cache */ @Bean + @ConditionalOnMissingBean(CacheManager.class) public CacheManager cacheManager() { return new PlusSpringCacheManager(); } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/AbstractPlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/AbstractPlusSpringCacheManager.java new file mode 100644 index 000000000..57a89dedc --- /dev/null +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/AbstractPlusSpringCacheManager.java @@ -0,0 +1,209 @@ +/** + * Copyright (c) 2024-2024 supreme + * + * 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 org.dromara.common.redis.manager; + +import org.dromara.common.redis.utils.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 CacheManager} implementation + * backed by Redisson instance. + *

+ * 修改 RedissonSpringCacheManager 源码 + * 重写 cacheName 处理方法 支持多参数 + * + * @author Nikita Koksharov + * + */ +@SuppressWarnings("unchecked") +public abstract class AbstractPlusSpringCacheManager 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 AbstractPlusSpringCacheManager() { + } + + + /** + * 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) { + name = getCacheNameWrapper(name); + + // 重写 cacheName 支持多参数 + String[] array = StringUtils.delimitedListToStringArray(name, "#"); + name = array[0]; + + 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); + } + + 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 = getCaffeineCacheDecorator(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 = getCaffeineCacheDecorator(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()); + } + + + /** + * 获取缓存名称包装 + * + * @param cacheName 缓存名称 + * @return {@link String } + */ + public abstract String getCacheNameWrapper(String cacheName); + + /** + * 获取Caffeine缓存装饰器 + * + * @param cache 缓存 + * @return {@link Cache } + */ + public abstract Cache getCaffeineCacheDecorator(Cache cache); +} diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java index ee1d405f2..d72954cd8 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java @@ -32,7 +32,7 @@ public class CaffeineCacheDecorator implements Cache { } public String getUniqueKey(Object key) { - return cache.getName() + ":" + key; + return getName() + ":" + key; } @Override diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index a48cb1422..cb4dc4f3e 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2013-2021 Nikita Koksharov + * Copyright (c) 2024-2024 supreme * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,178 +15,26 @@ */ package org.dromara.common.redis.manager; -import org.dromara.common.redis.utils.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 + * A {@link org.dromara.common.redis.manager.AbstractPlusSpringCacheManager} extends * backed by Redisson instance. *

- * 修改 RedissonSpringCacheManager 源码 - * 重写 cacheName 处理方法 支持多参数 - * - * @author Nikita Koksharov + * 重写 cacheName 处理方法 + * 重写 获取Caffeine缓存装饰器 * + * @author supreme */ -@SuppressWarnings("unchecked") -public class PlusSpringCacheManager implements CacheManager { +public class PlusSpringCacheManager extends AbstractPlusSpringCacheManager { - 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 String getCacheNameWrapper(String cacheName) { + return cacheName; } @Override - public Cache getCache(String name) { - // 重写 cacheName 支持多参数 - String[] array = StringUtils.delimitedListToStringArray(name, "#"); - name = array[0]; - - 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); - } - - 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); + public Cache getCaffeineCacheDecorator(Cache cache) { + return new CaffeineCacheDecorator(cache); } - - private Cache createMap(String name, CacheConfig config) { - RMap map = RedisUtils.getClient().getMap(name); - - Cache cache = new CaffeineCacheDecorator(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 CaffeineCacheDecorator(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()); - } - - } diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java index 07302bc81..923e4d96c 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/config/TenantConfig.java @@ -71,7 +71,6 @@ public class TenantConfig { /** * 多租户缓存管理器 */ - @Primary @Bean public CacheManager tenantCacheManager() { return new TenantSpringCacheManager(); diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantCaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantCaffeineCacheDecorator.java new file mode 100644 index 000000000..9898c876d --- /dev/null +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantCaffeineCacheDecorator.java @@ -0,0 +1,34 @@ +package org.dromara.common.tenant.manager; + +import cn.hutool.core.text.StrPool; +import org.dromara.common.core.constant.GlobalConstants; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.redis.manager.CaffeineCacheDecorator; +import org.dromara.common.tenant.helper.TenantHelper; +import org.springframework.cache.Cache; + +/** + * 重写 CaffeineCacheDecorator Name处理方法 支持多租户 + * + * @author Supreme + * @since 2024/07/25 + */ +public class TenantCaffeineCacheDecorator extends CaffeineCacheDecorator { + public TenantCaffeineCacheDecorator(Cache cache) { + super(cache); + } + + @Override + public String getName() { + String cacheName = super.getName(); + if (StringUtils.contains(cacheName, GlobalConstants.GLOBAL_REDIS_KEY)) { + return cacheName; + } + String tenantId = TenantHelper.getTenantId(); + if (StringUtils.startsWith(cacheName, tenantId)) { + // 如果存在则直接返回 + return cacheName; + } + return tenantId + StrPool.COLON + cacheName; + } +} diff --git a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java index d230afc1a..9d837b5e6 100644 --- a/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-tenant/src/main/java/org/dromara/common/tenant/manager/TenantSpringCacheManager.java @@ -1,32 +1,40 @@ package org.dromara.common.tenant.manager; +import cn.hutool.core.text.StrPool; import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.redis.manager.PlusSpringCacheManager; +import org.dromara.common.redis.manager.AbstractPlusSpringCacheManager; import org.dromara.common.tenant.helper.TenantHelper; import org.springframework.cache.Cache; /** * 重写 cacheName 处理方法 支持多租户 + * 重写 CaffeineCacheDecorator 获取方法 支持多租户 * - * @author Lion Li + * @author Supreme + * @since 2024/07/25 */ -public class TenantSpringCacheManager extends PlusSpringCacheManager { +public class TenantSpringCacheManager extends AbstractPlusSpringCacheManager { public TenantSpringCacheManager() { } + @Override - public Cache getCache(String name) { - if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { - return super.getCache(name); + public String getCacheNameWrapper(String cacheName) { + if (StringUtils.contains(cacheName, GlobalConstants.GLOBAL_REDIS_KEY)) { + return cacheName; } String tenantId = TenantHelper.getTenantId(); - if (StringUtils.startsWith(name, tenantId)) { + if (StringUtils.startsWith(cacheName, tenantId)) { // 如果存在则直接返回 - return super.getCache(name); + return cacheName; } - return super.getCache(tenantId + ":" + name); + return tenantId + StrPool.COLON + cacheName; } + @Override + public Cache getCaffeineCacheDecorator(Cache cache) { + return new TenantCaffeineCacheDecorator(cache); + } }