今天有人问我为什么我之前使用spring cache redis时,redis里面的key是cacheName::cacheKey格式,而现在cacheName的前缀没了?下面我们来分析一下key的生成逻辑。
源码分析
Spring Boot应用启动时,会自动加载redis相关的配置。我们主要关注一下RedisCacheConfiguration类。该类会创建一个RedisCacheManager。
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));@1
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
return this.customizerInvoker.customize(builder.build());
}
代码@1:通过Builder模式构建一个RedisCacheManagerBuilder。创建RedisCacheManagerBuilder会通过determineConfiguration()方法传入redis默认的配置。
private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(
ClassLoader classLoader) {
if (this.redisCacheConfiguration != null) {
return this.redisCacheConfiguration;
}
Redis redisProperties = this.cacheProperties.getRedis();
org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration
.defaultCacheConfig(); @1
config = config.serializeValuesWith(SerializationPair
.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));
if (redisProperties.getTimeToLive() != null) {
config = config.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
config = config.prefixKeysWith(redisProperties.getKeyPrefix()); @2
}
if (!redisProperties.isCacheNullValues()) {
config = config.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
config = config.disableKeyPrefix(); @3
}
return config;
}
public RedisCacheConfiguration prefixKeysWith(String prefix) {
Assert.notNull(prefix, "Prefix must not be null!");
return computePrefixWith((cacheName) -> prefix);
}
代码@1:创建一个默认的RedisCacheConfiguration。这里会定义一个默认的CacheKeyPrefix的实现CacheKeyPrefix.simple(),simple()方法其实就是返回了一个"cacheName::"字符串。
代码@2:如果key前缀不为空,设置key前缀。该方法就是返回一个CacheKeyPrefix实现为(cacheName) -> prefix的RedisCacheConfiguration。这里就可以看出,如果你开启了使用key前缀开关,并且你的key前缀不为空,默认的CacheKeyPrefix.simple()实现就被替换为(cacheName) -> prefix,此时,你的key前缀就不会用使用缓存名了。
代码@3:如果不允许使用key前缀,则关闭key前缀。
当调用RedisCache中方法时,用到缓存key的地方都会经过createAndConvertCacheKey方法。
private byte[] createAndConvertCacheKey(Object key) {
return serializeCacheKey(createCacheKey(key));@1
}
代码@1:序列化缓存key。
protected String createCacheKey(Object key) {
String convertedKey = convertKey(key);@1
if (!cacheConfig.usePrefix()) {@2
return convertedKey;
}
return prefixCacheKey(convertedKey);@3
}
代码@1:将key转为字符串。
代码@2:判断是否开启使用前缀开关,如果没开启,直接返回key作为缓存key。
代码@3:返回带有前缀的缓存key。
private String prefixCacheKey(String key) {
// allow contextual cache names by computing the key prefix on every call.
return cacheConfig.getKeyPrefixFor(name) + key;@1
}
代码@1:调用RedisCacheConfiguration中的CacheKeyPrefix获取前缀,再拼接传入的key作为缓存的key。
总结:
1、RedisCacheConfiguration#defaultCacheConfig()方法创建一个RedisCacheConfiguration,默认CacheKeyPrefix实现是CacheKeyPrefix.simle(),也就是返回一个“cacheName::”字符串作为前缀。
2、当开启了使用key前缀(spring.cache.redis.use-key-prefix=true)并且配置的key前缀(spring.cache.redis.key-prefix=xxx)不为空时,RedisCacheConfiguration使用(cacheName) -> prefix作为CacheKeyPrefix实现,此时你返回的前缀的值就是你定义的prefix字符串。