系统集成spring缓存。使用 @CacheEvict
进行缓存清除。 @CacheEvict
可以清除指定的key,可以指定allEntries = true
清除命名空间下的所有元素。现在我有一个问题。使用 allEntries = true
。清除命名空间的值只能是一个常量,但我现在需要根据租户唯一的TelnetID来分离缓存。这样就导致不能使用allEntries = true
,否则一旦触发缓存清除,所有缓存都会被清除,而我只想清除当前租户的缓存。熟悉Redis命令的人都知道,查询和删除都可以进行模糊匹配,所以我想要SpringCache的@CacheEvict
至还支持模糊匹配清除。
LLET的第一个弄清楚@cacheevict
如何实现缓存清洁,因为我以前已经看到了redis的源代码,并且知道@@cacheevict
是通过AOP和核心类实现的就是CacheAspectSupport
,具体源码分析细节大家可以自行Google,我只简单分析一下CacheAspectSupport
这个类中的几个关键方法
private对象execute(finalCacheOperationInvoker调用者,Method方法,CacheOperationContexts上下文) {
// 特殊处理同步调用
if (contexts.isSynchronized()) {
CacheOperationContext context = contexts.get(CacheableOperation.类).迭代器().下一个();
if(isConditionPassing(上下文,CacheOperation) www.sychzs.cn_RESULT)) {
对象键=generateKey(context, CacheOperationExpressionEvaluator. NO_RESULT);
缓存缓存= context.getCaches().iterator().next();
try {
returnwrapCacheValue(方法,缓存。 get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch(Cache.ValueRetrievalException ex){
//调用者换行ThrowableWrapper 实例中的任何 Throwable,因此我们
//确保其中一个在堆栈中冒泡。
throw(CacheOperationInvoker.ThrowableWrapper)ex.getCause();
} }
其他 {
//无需缓存,只需调用底层方法
returninvokeOperation(invoker); }
}
// 处理任何早期驱逐
processCacheEvicts(contexts.get(CacheEvictOperation.class),true ,
CacheOperationExpressionEvaluator.NO_RESULT);
//检查我们是否有与条件
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation) .class));
//从任何 @Cacheable 未命中收集 put,如果未找到缓存项目
列表cachePutRequests = new LinkedList<>();
if (cacheHit == null ) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator. NO_RESULT,cachePutRequests);
}
对象cacheValue;
对象returnValue;
if (cacheHit != null && !hasCachePut(上下文)) {
//如果没有put请求,就使用缓存命中
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method,cacheValue);
}
其他 {
//如果我们没有缓存命中,则调用该方法
returnValue = invokeOperation(invoker); cacheValue = unwrapReturnValue(returnValue);
}
//收集任何显式@CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue,cachePutRequests) ;
//处理任何收集的 put 请求,无论是来自 @CachePut 还是 @Cacheable miss
for (CachePutRequestcachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// 处理任何后期驱逐 ProcessCacheEvicts(contexts.get) (CacheEvictOperation.类), false, cacheValue);
return returnValue;
}
这个方法是缓存控制入口核心方法processCacheEvicts
会根据CacheOperationContext
进行缓存清理处理。我们可以看到一个名为 performCacheEvict Method
private void performCacheEvict(
CacheOperationContext 上下文,CacheEvictOp操作,@Nullable 对象结果) {
对象键 = null;
for(缓存缓存:context.getCaches()){
if (操作.isCacheWide()) { // 如果 allEntries 为 true,则执行此逻辑
logInvalidating(context, operation, null);
doClear(cache);
}
其他 { // 否则,删除指定键
if (key == null ) {
key =generateKey(上下文, 结果);
。
}
看到这里,我们就知道是怎么回事了。我们继续调试和跟进。我们看到doClear和doEvict最终会调用RedisCache
中的evict和clear方法@Override
public void evict(对象键) {
cacheWriter.remove(name, createAndConvertCacheKey (关键));
}
/*
* (非javadoc)
* @see org.springframework.cache.Cache#清除()
*/
@Override
public void 清除() {
//支持模糊删除
byte[] pattern = conversionService.convert(createCacheKey("*" ),字节[] .类);
cacheWriter.clean(名称, 模式);
}
我们看到如果allEntries为ture的时候最终执行的是clear()
这个方法,其实他也是模糊删除的,只是他的关键规则是namespace:: *
,看到这里就看到希望我们稀疏一个办法在namespace *中
插入我们的telnetID就可以变成namespace ::telnetID:*
这种格式,也就达到了我们的目的了。
重点需要重写RedisCache的evict方法,新建一个RedisCacheResolver
集成RedisCache,重写evict方法
public class RedisCacheResolver 扩展 RedisCache {
私有 final 字符串名称;
私有 final RedisCacheWriter cacheWriter;
private finalConversionService conversionService;
受保护 RedisCacheResolver(字符串名称,RedisCacheWritercacheWriter,RedisCacheConfiguration缓存配置) {
超级(名称,cacheWriter,cacheConfig);
这个.name =名称;
这个 .cacheWriter = cacheWriter;
这个.conversionService = cacheConfig.getConversionService();
}
/**
*
* @Title:驱逐
* @Description: 重写删除的方法
* @param @param key * @throws
*
*/
@Override
public void evict(对象键) {
//如果按键包含“ noCacheable :"关键字不会被缓存
if (key.toString().contains(www.sychzs.cn_CACHEABLE)) {
返回;
{
keyString key.toString (); if (StringUtils.endsWith(keyString, "*")) {
evictLikeSuffix(keyString);
evictLikeSuffix(key)
我被驱逐(键);
}
/**
* * 后缀匹配匹配
* *
* 私人 空 evictLikeSuffix(字符串键) {
byte[] 模式 = 这个 .conversionService.convert(这个.createCacheKey(key), byte[].class);
这个.cacheWriter。 clean(this.name,pattern);
}
}
现在我们需要制作这个 此时我们关键步骤都完成了,最后我们只需要在RedisConfig RedisCacheManagerResolver中管理自己的 通过在上面的步骤中,我们有实现重写 evict 方法,用于模糊删除缓存 只要使用上面的注释,我们就可以删除telnetID下的所有缓存Redis CacheResolver
生效,所以我们需要改变我们的RedisCacheResolver被注入到RedisCacheManager
,所以我们需要定义自己的RedisCacheManagerResolver
与z集成
public class RedisCacheManagerResolver 扩展 RedisCacheManager {
私有 finalRedisCacheWritercacheWriter;
私有 finalRe disCacheConfiguration defaultCacheConfig;
public RedisCacheManagerResolver( RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
这个.cacheWriter = cacheWriter;
这个.defaultCacheConfig = defaultCacheConfiguration;
}
public RedisCacheManagerResolver(RedisCacheWritercacheWriter,RedisCacheConfigurationdefaultCacheConfiguration,String...initialCacheNames){
super(cacheWriter,defaultCacheConfiguration,initialCacheNames);
这个.cacheWriter=cacheWriter;
这个.defaultCacheConfig = defaultCacheConfiguration;
}
public RedisCacheManagerResolver(RedisCacheWriter cacheWriter、 RedisCacheConfiguration defaultCacheConfiguration、布尔值allowInFlightCacheCreation,字符串...初始CacheNames){
超级 (cacheWriter,defaultCacheConfiguration,allowInFlightCacheCreation,initialCacheNames);
这个 .cacheWriter = cacheWriter;
这个.defaultCacheConfig = defaultCacheConfiguration;
}
public RedisCacheManagerResolver(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration , Map
super(cacheWriter, defaultCacheConfiguration , initialCacheConfigurations);
这个.cacheWriter = cacheWriter;
这个.defailureCacheConfig = defaultCacheConfiguration;
}
public RedisCacheManagerResolver(RedisCacheWritercacheWriter、RedisCacheConfigurationdefaultCacheConfiguration、Map
超级(cacheWriter、defaultCacheConfiguration、initialCacheConfigurations、allowInFlightCacheCreation); 这个.cacheWriter = cacheWriter;
这个.defaultCacheConfig = defaultCacheConfiguration;
}
public RedisCacheManagerResolver(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration cacheConfiguration) {
这个(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),cacheConfiguration);
}
/**
*覆盖父类创建RedisCache,采用自定义的RedisCacheResolver
* @标题:createRedisCache
* @Description:TODO
* @param @param 姓名
* @param @paramcacheConfig
* @param @return * @扔
*
*/
@Override
受保护 RedisCache createRedisCache(字符串名称,@Nullable RedisCacheConfig缓存配置) {
返回 newRedisCacheResolver(名称,cacheWriter,cacheConfig!=null?cacheConfig:defaultCacheConfig);
}
@Override
public 地图
Map
getCacheNames().forEach(it -> {
RedisCache缓存= RedisCacheResolver.类.演员() LookupCache(它? null);
});
返回 Collections.unmodifyingMap(configurationMap);
}
}就
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//设置全局过期时间,单位为秒
Duration timeToLive = www.sychzs.cn;
timeToLive = Duration.ofSeconds(timeOut);
// RedisCacheManager cacheManager = new RedisCacheManager(
// RedisCacheWriter.nonLockingRedisCacheWriter(re disConnectionFactory),
// this.getRedisCacheConfig urationWithTtl(生存时间), // 默认策略,未配置的值就使用这个
// this.getRedisCacheConfigurationMap() // 指定值策略
// );
RedisCacheManagerResolver cacheManager =
new RedisCacheManagerResolver(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
这个.getRedisCacheConfigurationWithTtl(timeToLive), //默认策略,未配置的值就使用这个
.getRedisCacheConfigurationMap() // 指定取值策略
);
// RedisCacheManager cacheManager=RedisCacheManager.builder(factory).cacheDefaults(config).build();
returncacheManager;
}@CacheEvict(value = "BASE_CACHE, key = "#modelClassName + #telenetId+ '*'", allEntries =假)
打造高质量的技术交流社区。欢迎从事编程开发、技术招聘的HR人员加入。也欢迎大家分享自己公司的内部推荐信息,互相帮助,共同进步!