虽然以前就集成了SpringCache redis缓存数据,但在此项目的缓存中存在禁用缓存清除的问题。 我花了时间好好整理了一下。 主要是对@CacheEvict的key模糊匹配的支持。 熟悉redis命令的人知道查询和删除都可以进行模糊匹配,因此我们希望SpringCache也支持模糊匹配的清除。
契机是,由于缓存了向外部公开的API接口,API需要支持分页,因此在@Cacheable的key中,在current (当前页码)和classify (分类)中进行了匹配。 更改数据集时,每次删除所有分类的所有密钥都有点不合理。 如果只是更改了数据并针对某个分类清理密钥,key
开始行动,调查SpringCache的源代码和原理,具体可以在网上查找。 我只想说重点。
找到RedisCache中的evict后,对应于@CacheEvict的最终执行方法。 看到下面有clear方法,使用了模糊匹配的批量清除。 我觉得有点戏。
/**(non-JavaDoc ) @ see org.spring framework.cache.cache # evict ) Java.lang.object )/@ override公共语音///
}
/* * (非javadoc ) @ see org.spring framework.cache.cache # clear ) (*/@Overridepublic voidclear () ) ) )
byte [ ] pattern=conversion service.convert (create cachekey )、byte[].class );
cachewriter.clean(name,pattern ); //clean支持模糊匹配。 这是扩张时的重点
}
1 .先写RedisCache的子类,实际代替RedisCache
publicclasscustomizedrediscacheextendsrediscache {
私密金融字符串名称;
私有金融体系;
私密性转换服务;
protectedcustomizedrediscache (string name,RedisCacheWriter cacheWriter,rediscacheconfigurationcacheconfig ) super ) name
this.name=name;
this.cacheWriter=cacheWriter;
this.conversion service=cache config.getconversionservice (;
}
@ overridepublicvoidevict (object key ) if ) keyinstanceofstring ) {String keyString=key.toString;
if (string utils.endswith (密钥字符串,' * ' ) Evictlikesuffix (密钥字符串) );
返回;
} if (string utils.starts with (key string,' * ' ) evictlikeprefix (key string ) );
返回;
}super.evict(key );
}
/***前缀匹配* @ param key */publicvoidevictlikeprefix (string key ) byte [ ] pattern=this.conversion service.convert
this.cache writer.clean (this.name,pattern );
}
/***匹配后缀* @ param key */publicvoidevictlikesuffix (string key ) byte (pattern=) byte ) ) this.conversionservice
this.cache writer.clean (this.name,pattern );
}
//不需要前后一致的,直接allEntries=true
}
2 .如何让定制的客户参与进来
换RedisCache,还必须弄个RedisCacheManager的子类,RedisCache的创建过程是在manager中public classCustomizedRedisCacheManager extendsRedisCacheManager {
private finalRedisCacheWriter cacheWriter;
private finalRedisCacheConfiguration defaultCacheConfig;
private finalMapinitialCaches= newLinkedHashMap<>();
private booleanenableTransactions;
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
this.cacheWriter= cacheWriter;
this.defaultCacheConfig= defaultCacheConfiguration;
}
publicCustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, booleanallowInFlightCacheCreation, String... 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) {this(RedisCacheWriter.nonLockingRedisCacheWriter(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? cache.getCacheConfiguration(): 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().computePrefixWith(cacheName -> "demo".concat(":").concat(cacheName).concat(":")).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(newStringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(createjkdwd2JsonRedisSerializer(objectMapper)));
return newCustomizedRedisCacheManager(redisConnectionFactory,cacheConfiguration);
//改变CacheManager的创建方式,原方式如下
// returnRedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();
}
privateRedisSerializercreatejkdwd2JsonRedisSerializer(ObjectMapper objectMapper) {//TODO Auto-generated method stubjkdwd2JsonRedisSerializerserializer = newjkdwd2JsonRedisSerializer(Object.class);
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
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的所有分页缓存数据