ruoyi-vue-plus-缓存功能
# 注解缓存
Spring提供了 SpringCache 注解缓存机制 , 能够在方法级别进行缓存数据
涉及注解
注解 | 说明 |
---|---|
@Cacheable | 缓存方法返回数据 , 下次调用不会进入方法体 |
@CachePut | 更新缓存 , 将返回数据覆盖原有的数据 |
@CacheEvict | 清除缓存 (最好追加清除条件) |
@Caching | 组合缓存 , 组合以上注解共同实现 |
常用注解参数说明
参数值 | 说明 |
---|---|
String[] value | cacheNames别名应用 |
String[] cacheNames | 缓存key名称 |
String key | 缓存field名称 (Map中的Key) (支持 SpEL语法) |
String condition | 条件缓存 , 在方法执行前判断 , 满足则缓存 (支持 SpEL语法) |
String unless | 条件缓存 , 在方法执行后判断 , 满足则不缓存 (支持 SpEL语法) |
boolean sync | 方法同步 . 确保高并发时 , 首次访问缓存后 , 下一次访问缓存必须生效 (避免一次性击穿问题) |
支持SpEL语法 可以控制拿到 请求参数 , 响应数据
- 请求参数获取 :
#参数名
- 响应数据获取 :
#result
# 应用前置配置
RedisConfig
配置类中 启用缓存管理功能@EanbleCaching
注解
/**
* 自定义缓存管理器 整合spring-cache
*/
@Bean
public CacheManager cacheManager() {
return new PlusSpringCacheManager();
}
# PlusSpringCacheManager类 缓存管理
模仿 RedissonSpringCacheManager类源码 重写 cacheName 处理方法
核心接口 :
- Cache 缓存对象操作接口 , 为缓存对象定义规范 , 包含各种缓存操作集合
- CacheManager 提供各种xxxCache的实现 (支持其他缓存 , 如Redis)
意图 : 实现控制控制缓存 过期时间 / 活跃过期 / 最大缓存 缓存配置 , 在设置CacheNames时配置Redis , 使其缓存更合理的使用
例子 : test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
顺序 | 标识 | 单位 | 说明 |
---|---|---|---|
1 | ttl | 时长(s,m,h,d) | 过期时间 (0则不过期 默认为0) |
2 | maxIdleTime | 时长(s,m,h,d) | 最大空闲时间 指定活跃过期 (0则不检测 默认为0) |
3 | maxSize | 数值 | 缓存最大长度 (0则无限长 默认为0) |
注意
- 一定要遵循顺序配置以上信息
- maxSize 一旦超过该数值会将最旧的数据进行清空
源码
核心实现方法 PlusSpringCacheManager#getCache()
点击展开
@SuppressWarnings("unchecked")
public class PlusSpringCacheManager implements CacheManager {
// 是否动态缓存
private boolean dynamic = true;
// 是否允许缓存存null值
private boolean allowNullValues = true;
// 是否使用事务
private boolean transactionAware = true;
/**
* 一样对应的 缓存配置 和 缓存数据
* 采用 ConcurrentHashMap 保证线程安全
*/
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) {
// 重写 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);
}
// 缓存数据控制 1:过期时间 , 2:最大空闲时间, 3:最大缓存数
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());
}
}
# 实现应用
仅作示例 , 以下示例 为了方便演示 在Controller层实现 , 一般情况会在 Service实现类的方法写 注解缓存
点击展开
@RestController
@SaIgnore
@RequestMapping("/demo/cache")
public class TestRedisCacheController {
// test
@Cacheable(value = "/test")
@GetMapping("/t1")
public String test01(SysUser user) {
System.out.println("user = " + user);
return "t1";
}
// 成员变量测试
@Cacheable(value = "test2" , key = "#user.userId")
@GetMapping("/t2")
public String test02(SysUser user) {
System.out.println("user2 = " + user);
return "t2";
}
// 普通方法测试
@Cacheable(value = "test3" , key = "#user.getTestUserId()")
@GetMapping("/t3")
public String test03(SysUser user) {
System.out.println("user3 = " + user);
return "t3";
}
// JSON测试
@Cacheable(value = "test4" , key = "#user.getTestUserId()")
@PostMapping("/t4")
public String test04(@RequestBody SysUser user) {
System.out.println("user4 = " + user);
return "t4";
}
// 套娃对象测试赛
@Cacheable(value = "test5" , key = "#user.student.getTestUserId()")
@PostMapping("/t5")
public String test05(@RequestBody SysUser user) {
System.out.println("user5 = " + user);
return "t5";
}
// 判断控制
@Cacheable(value = "test6" , key = "#user.student.getTestUserId()" , condition = "#user.userId != null")
@PostMapping("/t6")
public String test06(@RequestBody SysUser user) {
System.out.println("user6 = " + user);
return "t6";
}
// 判断控制
@Cacheable(value = "test7" , key = "#user.userId" , unless = "#result == null")
@PostMapping("/t7")
public String test07(@RequestBody SysUser user) {
System.out.println("user7 = " + user);
return user.getUserName();
}
// 异步测试
@Cacheable(value = "test8" , key = "#user.userId" , sync = true)
@PostMapping("/t8")
public String test08(@RequestBody SysUser user) {
System.out.println("user8 = " + user);
ThreadUtil.sleep(5000);
return user.getUserName();
}
// 携带Redis基础信息配置缓存
@Cacheable(cacheNames = "test09#60s#10m#20", key = "#key", condition = "#key != null")
@GetMapping("/t9")
public R<String> test09(String key, String value) {
return R.ok("操作成功", value);
}
// 更新缓存
@CachePut(cacheNames = "test10#60s#10m#20", key = "#key", condition = "#key != null")
@GetMapping("/t10")
public R<String> test10(String key, String value) {
return R.ok("操作成功", value);
}
// 清除缓存
@CacheEvict(cacheNames = "test10", key = "#key", condition = "#key != null")
@GetMapping("/t11")
public R<String> test11(String key, String value) {
return R.ok("操作成功", value);
}
}
# 组合式缓存
组合式缓存
@Caching
注解 , 一个方法多项缓存策略 , 在参数中再次写 @Cacheable
/ @CachePut
/ @CacheEvict
缓存
Caching注解参数 | 说明 |
---|---|
Cacheable[] cacheable | 存 缓存注解 |
CachePut[] put | 更新 缓存注解 |
CacheEvict[] evict | 移除 缓存注解 |