Pre Merge pull request !572 from hfdy2019/dev

This commit is contained in:
hfdy2019 2024-08-02 23:31:42 +00:00 committed by Gitee
commit e7f4d8c90f
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 274 additions and 174 deletions

View File

@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import org.dromara.common.redis.manager.PlusSpringCacheManager; import org.dromara.common.redis.manager.PlusSpringCacheManager;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -38,6 +39,7 @@ public class CacheConfig {
* 自定义缓存管理器 整合spring-cache * 自定义缓存管理器 整合spring-cache
*/ */
@Bean @Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager cacheManager() { public CacheManager cacheManager() {
return new PlusSpringCacheManager(); return new PlusSpringCacheManager();
} }

View File

@ -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.
* <p>
* 修改 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<String, CacheConfig> configMap = new ConcurrentHashMap<>();
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
/**
* Creates CacheManager supplied by Redisson instance
*/
public AbstractPlusSpringCacheManager() {
}
/**
* 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) {
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<Object, Object> 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<Object, Object> 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<String> 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);
}

View File

@ -32,7 +32,7 @@ public class CaffeineCacheDecorator implements Cache {
} }
public String getUniqueKey(Object key) { public String getUniqueKey(Object key) {
return cache.getName() + ":" + key; return getName() + ":" + key;
} }
@Override @Override

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,178 +15,26 @@
*/ */
package org.dromara.common.redis.manager; 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.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. * backed by Redisson instance.
* <p> * <p>
* 修改 RedissonSpringCacheManager 源码 * 重写 cacheName 处理方法
* 重写 cacheName 处理方法 支持多参数 * 重写 获取Caffeine缓存装饰器
*
* @author Nikita Koksharov
* *
* @author supreme
*/ */
@SuppressWarnings("unchecked") public class PlusSpringCacheManager extends AbstractPlusSpringCacheManager {
public class PlusSpringCacheManager implements CacheManager {
private boolean dynamic = true; @Override
public String getCacheNameWrapper(String cacheName) {
private boolean allowNullValues = true; return cacheName;
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 @Override
public Cache getCache(String name) { public Cache getCaffeineCacheDecorator(Cache cache) {
// 重写 cacheName 支持多参数 return new CaffeineCacheDecorator(cache);
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<Object, Object> 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<Object, Object> 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<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet());
}
} }

View File

@ -71,7 +71,6 @@ public class TenantConfig {
/** /**
* 多租户缓存管理器 * 多租户缓存管理器
*/ */
@Primary
@Bean @Bean
public CacheManager tenantCacheManager() { public CacheManager tenantCacheManager() {
return new TenantSpringCacheManager(); return new TenantSpringCacheManager();

View File

@ -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;
}
}

View File

@ -1,32 +1,40 @@
package org.dromara.common.tenant.manager; package org.dromara.common.tenant.manager;
import cn.hutool.core.text.StrPool;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.utils.StringUtils; 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.dromara.common.tenant.helper.TenantHelper;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
/** /**
* 重写 cacheName 处理方法 支持多租户 * 重写 cacheName 处理方法 支持多租户
* 重写 CaffeineCacheDecorator 获取方法 支持多租户
* *
* @author Lion Li * @author Supreme
* @since 2024/07/25
*/ */
public class TenantSpringCacheManager extends PlusSpringCacheManager { public class TenantSpringCacheManager extends AbstractPlusSpringCacheManager {
public TenantSpringCacheManager() { public TenantSpringCacheManager() {
} }
@Override @Override
public Cache getCache(String name) { public String getCacheNameWrapper(String cacheName) {
if (StringUtils.contains(name, GlobalConstants.GLOBAL_REDIS_KEY)) { if (StringUtils.contains(cacheName, GlobalConstants.GLOBAL_REDIS_KEY)) {
return super.getCache(name); return cacheName;
} }
String tenantId = TenantHelper.getTenantId(); 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);
}
} }