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

This commit is contained in:
疯狂的狮子Li 2022-08-13 11:30:33 +08:00
parent 5d367b7bf8
commit 89c1e4f91d
6 changed files with 230 additions and 76 deletions

View File

@ -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 毫秒

View File

@ -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#60stest#0#60stest#0#1m#1000test#1h#0#500
*
* @author Lion Li
*/
public interface CacheNames {
/**
* 演示案例
*/
String DEMO_CACHE = "demo:cache#60s#10m#20";
}

View File

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

View File

@ -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();
}
/**

View File

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

View File

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