javamap模糊匹配key_SpringCache扩展@CacheEvict的key
模糊匹配清除
之前就⼀直集成SpringCache+redis缓存数据,这次项⽬中的缓存⼀直有缓存清除失效的问题,我就花时间好好梳理了⼀番,主要是针对@CacheEvict的key模糊匹配⽀持。熟悉redis命令的⼈都知道,查询和删除都可以做模糊匹配,所以我就想让SpringCache也⽀持模糊匹配清除。
起因是对外发布的API接⼝做了缓存,API需要⽀持分页,所以在@Cacheable的key中⽤current(当前页码)和classify(分类)做了匹配,在改动数据集的时候,如果每次都把所有分类的key全部清除掉有点不合理,如果只是针对改动数据集中某个分类做key的清理,这样最节省资源。
开始⾏动,查看SpringCache源码和原理,具体的可以⽹上查,我只想说重点。
到RedisCache中的evict,对应的就是@CacheEvict最终的执⾏⽅法,看到下⾯有个clear⽅法,使⽤的就是模糊匹配批量清除,⼀下觉得有戏。
/** (non-Javadoc)* @see org.springframework.cache.Cache#evict(java.lang.Object)*/@Overridepublic voidevict(Object key) {ve(name, createAndConvertCacheKey(key)); //remove只能针对单个key删除操作
}
/** (non-Javadoc)* @see org.springframework.cache.Cache#clear()*/@Overridepublic voidclear() {
byte[]pattern = vert(createCacheKey("*"), byte[].class);
cacheWriter.clean(name, pattern); //clean⽀持模糊匹配,这个是扩展的时候重点
}
1.先写个RedisCache⼦类,在实际中替代RedisCache
public classCustomizedRedisCache extendsRedisCache {
private finalString name;
private finalRedisCacheWriter cacheWriter;
private finalConversionService conversionService;
protectedCustomizedRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {super(name, cacheWriter, cacheConfig);
this.name= name;
this.cacheWriter=cacheWriter;
}
@Overridepublic voidevict(Object key) {if(key instanceofString){String String();
dsWith(keyString,"*")){evictLikeSuffix(keyString);
return;
}if(StringUtils.startsWith(keyString,"*")){evictLikePrefix(keyString);
return;
}}super.evict(key);
}
/
*** 前缀匹配*@paramkey*/public voidevictLikePrefix(String key) {byte[]pattern =
this.cacheWriter.clean(this.name, pattern);
}
/*** 后缀匹配*@paramkey*/public voidevictLikeSuffix(String key) {byte[]pattern =
(byte[])ateCacheKey(key), byte[].class);
this.cacheWriter.clean(this.name, pattern);
}
//前后都匹配的就不需要了,直接allEntries=true
}
2.要想让⾃定义的CustomizedRedisCache替换RedisCache,还必须弄个RedisCacheManager的⼦类,RedisCache的创建过程是在manager中
public classCustomizedRedisCacheManager extendsRedisCacheManager {
private finalRedisCacheWriter cacheWriter;
private finalRedisCacheConfiguration defaultCacheConfig;
private finalMapinitialCaches= newLinkedHashMap<>();
private booleanenableTransactions;
cacheablepublicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, initialCacheNames) {super(cacheWriter, defaultCacheConfiguratio
n, initialCacheNames);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, booleanallowInFlightCacheCreation, initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, MapinitialCacheConfigurations) {super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration
defaultCacheConfiguration, MapinitialCacheConfigurations, booleanallowInFlightCacheCreation) {super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
/**
* 这个构造⽅法最重要
**/
publicCustomizedRedisCacheManager(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration cacheConfiguration) {LockingRedisCacheWriter(redisConnectionFactory),cacheConfiguration);
}
//覆盖⽗类创建RedisCache
@OverrideprotectedRedisCache createRedisCache(String name, @NullableRedisCacheConfiguration cacheConfig) {return newCustomizedRedisCache(name, cacheWriter, cacheConfig != null? cacheConfig : defaultCacheConfig);
}
@OverridepublicMapgetCacheConfigurations() {MapconfigurationMap = newHashMap<>(getCacheNames().size());
getCacheNames().forEach(it -> {RedisCache cache = CustomizedRedisCache.class.cast(lookupCache(it));
configurationMap.put(it, cache != null? CacheConfiguration(): null);
});
returnCollections.unmodifiableMap(configurationMap);
}}
3.配置
/*** 缓存的序列化*@param*@paramredisConnectionFactory*@return*/@BeanpublicCacheManager
cacheManager(RedisConnectionFactory redisConnectionFactory) {ObjectMapper objectMapper = newObjectMapper();
RedisCacheConfiguration cacheConfiguration =
RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60*60)).disableCachingNullValues()putePrefixWit ->
"demo".concat(":").concat(cacheName).concat(":")).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(newS
return newCustomizedRedisCacheManager(redisConnectionFactory,cacheConfiguration);
//改变CacheManager的创建⽅式,原⽅式如下
// returnRedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();
}
privateRedisSerializercreateJackson2JsonRedisSerializer(ObjectMapper objectMapper) {//TODO Auto-generated method
stub\Jackson2JsonRedisSerializerserializer = newJackson2JsonRedisSerializer(Object.class);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
serializer.setObjectMapper(objectMapper);
returnserializer;
}
经过3步对RedisCache和RedisCacheManager后,运⾏后使⽤的就是⾃定义的CustomizedRedisCache和CustomizedRedisCacheManager了,是不是很简单。但实际是看完了整个SpringCache原理后做的最⼩改动。
使⽤@Cacheable⽐如: @Cacheable(value="demo", key="T(String).valueOf(#page.current).concat('-
classify').concat(#classify)")
使⽤@CacheEvict就可以:@CacheEvict(value = "demo", key="'*-classify'.concat(#classify)"),删除指定classify的所有分页缓存数据

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。