1.spring cache解析
1.1.RedisCache和RedisCacheManager
1.1.1.结构
1.1.2.解析
RedisCache使用RedisCacheWriter接口,用于对redis的读写;
RedisCacheWriter
RedisCacheWriter和Cache接口的差异:
所有方法都需要指定name,执行redis命令时如需加锁,name为锁的key;
RedisCacheWriter可根据正则清除缓存;
public interface RedisCacheWriter {
//**静态方法,创建不带锁的RedisCacheWriter实例
static RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return new DefaultRedisCacheWriter(connectionFactory);
}
//**静态方法,创建带锁的RedisCacheWriter实例
static RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return new DefaultRedisCacheWriter(connectionFactory, Duration.ofMillis(50));
}
//**保存键值对,可指定过期时间
void put(String name, byte[] key, byte[] value, @Nullable Duration ttl);
//**根据key获取value
byte[] get(String name, byte[] key);
//**如果键值对不存在则保存,返回缓存中当前的value
byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl);
//**删除键值对
void remove(String name, byte[] key);
//**清除符合正则的缓存
void clean(String name, byte[] pattern);
}
DefaultRedisCacheWriter
DefaultRedisCacheWriter是DefaultRedisCacheWriter接口唯一的实现类;
DefaultRedisCacheWriter是作用域为包内可见,用户无法创建,只能使用DefaultRedisCacheWriter接口提供的静态方法;
如果sleepTime为Zero或者为负数,则不会加锁,否则在执行redis命令时会加锁,默认为Zero;
class DefaultRedisCacheWriter implements RedisCacheWriter {
//**redis连接工厂
private final RedisConnectionFactory connectionFactory;
//**等待锁,在有锁时线程休眠sleepTime毫秒,然后重新获取锁。如果sleepTime为Zero或者为负数,则不会加锁,否则在执行redis命令时会加锁
private final Duration sleepTime;
//**设置键值对
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
...
//**获取到锁后执行
execute(name, connection -> {
//**如果ttl有效,则保存键值对并设置有效期
if (shouldExpireWithin(ttl)) {
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
} else { //**否则,则保存键值对
connection.set(key, value);
}
return "OK";
});
}
//**如果键值对不存在则保存,返回缓存中当前的value
public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
...
//**获取到锁后执行
return execute(name, connection -> {
//**如果需要加锁,则加锁
if (isLockingCacheWriter()) {
doLock(name, connection);
}
try {
//**键值对不存在时,设置键值对
if (connection.setNX(key, value)) {
//**如果ttl有效,则设置有效期
if (shouldExpireWithin(ttl)) {
connection.pExpire(key, ttl.toMillis());
}
return null;
}
//**返回当前保存的值
return connection.get(key);
} finally {
//**解锁
if (isLockingCacheWriter()) {
doUnlock(name, connection);
}
}
});
}
//**清除符合正则的缓存
public void clean(String name, byte[] pattern) {
...
//**获取到锁后执行
execute(name, connection -> {
//**是否加锁成功
boolean wasLocked = false;
try {
//**如果需要加锁,则加锁,并设置加锁成功
if (isLockingCacheWriter()) {
doLock(name, connection);
wasLocked = true;
}
//**根据正则获取key
byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
.toArray(new byte[0][]);
//**如果有配置的key,则全部删除
if (keys.length > 0) {
connection.del(keys);
}
} finally {
//**如果加锁成功并且需要加锁,则解锁
if (wasLocked && isLockingCacheWriter()) {
doUnlock(name, connection);
}
}
return "OK";
});
}
//**加锁
void lock(String name) {
execute(name, connection -> doLock(name, connection));
}
//**解锁
void unlock(String name) {
executeLockFree(connection -> doUnlock(name, connection));
}
//**加锁
private Boolean doLock(String name, RedisConnection connection) {
return connection.setNX(createCacheLockKey(name), new byte[0]);
}
//**解锁
private Long doUnlock(String name, RedisConnection connection) {
return connection.del(createCacheLockKey(name));
}
//**检查是否加锁
boolean doCheckLock(String name, RedisConnection connection) {
return connection.exists(createCacheLockKey(name));
}
//**是否加锁
private boolean isLockingCacheWriter() {
return !sleepTime.isZero() && !sleepTime.isNegative();
}
//**执行redis命令(没有获取到锁时会一直等待,直到获取锁,然后执行redis命令)
private T execute(String name, Function callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
checkAndPotentiallyWaitUntilUnlocked(name, connection);
return callback.apply(connection);
} finally {
connection.close();
}
}
//**执行redis命令
private void executeLockFree(Consumer callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
callback.accept(connection);
} finally {
connection.close();
}
}
//**等待锁(有锁时线程休眠sleepTime毫秒,然后重新获取锁)
private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
if (!isLockingCacheWriter()) {
return;
}
try {
while (doCheckLock(name, connection)) {
Thread.sleep(sleepTime.toMillis());
}
} catch (InterruptedException ex) {
// Re-interrupt current thread, to allow other participants to react.
Thread.currentThread().interrupt();
throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name),
ex);
}
}
//**ttl是否有有效
private static boolean shouldExpireWithin(@Nullable Duration ttl) {
return ttl != null && !ttl.isZero() && !ttl.isNegative();
}
//**根据name创建锁的key
private static byte[] createCacheLockKey(String name) {
return (name + "~lock").getBytes(StandardCharsets.UTF_8);
}
}
RedisCacheConfiguration
RedisCache可通过RedisCacheConfiguration配置常用选项;
RedisCacheConfiguration使用了建造者模式,由于RedisCacheConfiguration各项属性都为final,所以先初始化所有属性创建一个默认的对象,然后在每个初始化属性的方法中都是重新创建一个对象;
创建示例:RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
public class RedisCacheConfiguration {
//**缓存有效期
private final Duration ttl;
//**是否缓存空值
private final boolean cacheNullValues;
//**key前缀计算器
private final CacheKeyPrefix keyPrefix;
//**是否使用前缀
private final boolean usePrefix;
//**key的序列化器,持有RedisSerializer的引用,key只能是string
private final SerializationPair keySerializationPair;
//**value的序列化器,持有RedisSerializer的引用,value可以是任意类型
private final SerializationPair valueSerializationPair;
//**spring ConversionService类型转换
private final ConversionService conversionService;
//**静态方法,返回默认的RedisCacheConfiguration实例
public static RedisCacheConfiguration defaultCacheConfig() {
return defaultCacheConfig(null);
}
//**静态方法,返回默认的RedisCacheConfiguration实例
//**创建一个无过期时间,允许缓存空值,key需使用前缀且前缀为"name::"格式,key序列化器为StringRedisSerializer,
//**value序列化器这jdk序列化器,conversionService为DefaultFormattingConversionService的RedisCacheConfiguration
public static RedisCacheConfiguration defaultCacheConfig(@Nullable ClassLoader classLoader) {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
registerDefaultConverters(conversionService);
return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
SerializationPair.fromSerializer(RedisSerializer.string()),
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
}
//**创建一个RedisCacheConfiguration,并设置缓存过期时间
public RedisCacheConfiguration entryTtl(Duration ttl) {
Assert.notNull(ttl, "TTL duration must not be null!");
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
valueSerializationPair, conversionService);
}
...一系列的建造者模式初始化属性的方法
//**根据cacheName生成key的前缀
public String getKeyPrefixFor(String cacheName) {
Assert.notNull(cacheName, "Cache name must not be null!");
return keyPrefix.compute(cacheName);
}
//**conversionService中添加Converter
public void addCacheKeyConverter(Converter, String> cacheKeyConverter) {
configureKeyConverters(it -> it.addConverter(cacheKeyConverter));
}
//**conversionService配置
public void configureKeyConverters(Consumer registryConsumer) {
...
registryConsumer.accept((ConverterRegistry) getConversionService());
}
//**注册两个Converter
public static void registerDefaultConverters(ConverterRegistry registry) {
Assert.notNull(registry, "ConverterRegistry must not be null!");
registry.addConverter(String.class, byte[].class, source -> source.getBytes(StandardCharsets.UTF_8));
registry.addConverter(SimpleKey.class, String.class, SimpleKey::toString);
}
}
RedisCache
使用RedisCacheWriter操作redis读写;
使用cacheConfig的keySerializationPair和valueSerializationPair对key和value进行序列化和反序列化;
public class RedisCache extends AbstractValueAdaptingCache {
//**使用jdk序列化器序列化NullValue
private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
//**cache的name
private final String name;
//**RedisCacheWriter实例
private final RedisCacheWriter cacheWriter;
//**redis配置
private final RedisCacheConfiguration cacheConfig;
//**spring ConversionService类型转换
private final ConversionService conversionService;
//**获取value,返回object对象
protected Object lookup(Object key) {
//**使用cacheWriter获取value
byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
if (value == null) {
return null;
}
//**cacheConfig的value序列化器反序列化value
return deserializeCacheValue(value);
}
//**获取value,返回object对象,如果value不存在,则使用valueLoader生成value
public synchronized T get(Object key, Callable valueLoader) {
//**调用lookup方法,获取value,并转换为ValueWrapper对象
ValueWrapper result = get(key);
//**如果value不为空,则返回value,并强制转换为指定类型
if (result != null) {
return (T) result.get();
}
//**如果value为空,则把valueLoader的结果做为value保存,并返回
T value = valueFromLoader(key, valueLoader);
put(key, value);
return value;
}
//**保存键值对
public void put(Object key, @Nullable Object value) {
//**对value进行转换,如果value为null并且允许空值,则转换为NullValue
Object cacheValue = preProcessCacheValue(value);
//**如果value为null并且不允许空值,则抛出异常
if (!isAllowNullValues() && cacheValue == null) {
throw new IllegalArgumentException(String.format(
"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
name));
}
//**使用cacheConfig的value序列化器序列化value,然后使用cacheWriter保存键值对,并设置过期时间
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
//**删除键值对
public void evict(Object key) {
cacheWriter.remove(name, createAndConvertCacheKey(key));
}
//**清除所有键值对
public void clear() {
byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class);
cacheWriter.clean(name, pattern);
}
//**对value进行转换,如果value为null并且允许空值,则转换为NullValue
protected Object preProcessCacheValue(@Nullable Object value) {
if (value != null) {
return value;
}
return isAllowNullValues() ? NullValue.INSTANCE : null;
}
//**使用cacheConfig的key序列化器序列化key
protected byte[] serializeCacheKey(String cacheKey) {
return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
}
//**使用cacheConfig的value序列化器序列化value
protected byte[] serializeCacheValue(Object value) {
if (isAllowNullValues() && value instanceof NullValue) {
return BINARY_NULL_VALUE;
}
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}
//**使用cacheConfig的value序列化器反序列化value
protected Object deserializeCacheValue(byte[] value) {
if (isAllowNullValues() && ObjectUtils.nullSafeEquals(value, BINARY_NULL_VALUE)) {
return NullValue.INSTANCE;
}
return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
}
//**根据指定的key,生成保存在redis中的key
protected String createCacheKey(Object key) {
//**把key转换为string
String convertedKey = convertKey(key);
if (!cacheConfig.usePrefix()) {
return convertedKey;
}
//**如果key需要前缀,则使用cacheConfig的keyPrefix计算器计算key值,默认"name::key"
return prefixCacheKey(convertedKey);
}
//**把key转换为string
protected String convertKey(Object key) {
//**如果key为string类型,则直接返回
if (key instanceof String) {
return (String) key;
}
TypeDescriptor source = TypeDescriptor.forObject(key);
//**如果key能转换为string,则转换为string,并返回
if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
...
return conversionService.convert(key, String.class);
...
}
//**调用key的toString方法转换为string,并返回
Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
return key.toString();
}
...
}
}
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
//**redis读写
private final RedisCacheWriter cacheWriter;
//**redis配置
private final RedisCacheConfiguration defaultCacheConfig;
//**初始化缓存配置
private final Map initialCacheConfiguration;
//**name对应的cache不存在时,是否允许创建新的cache
private final boolean allowInFlightCacheCreation;
//**静态方法,根据connectionFactory创建默认的RedisCacheManager
public static RedisCacheManager create(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return new RedisCacheManager(new DefaultRedisCacheWriter(connectionFactory),
RedisCacheConfiguration.defaultCacheConfig());
}
//**建造者模式,根据connectionFactory创建RedisCacheManagerBuilder
public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
}
//**建造者模式,根据cacheWriter创建RedisCacheManagerBuilder
public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
Assert.notNull(cacheWriter, "CacheWriter must not be null!");
return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
}
//**根据初始化缓存配置加载cache
protected Collection loadCaches() {
List caches = new LinkedList<>();
for (Map.Entry entry : initialCacheConfiguration.entrySet()) {
caches.add(createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
//**如果name对应的cache不存在时,并且允许创建缓存,则根据defaultCacheConfig创建新的cache
protected RedisCache getMissingCache(String name) {
return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
}
//**根据name创建RedisCache
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
//**建造者
public static class RedisCacheManagerBuilder {
private final RedisCacheWriter cacheWriter;
//**创建默认的RedisCacheConfiguration
private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
//**默认不初始化cache
private final Map initialCaches = new LinkedHashMap<>();
//**是否启用事务
private boolean enableTransactions;
//**name对应的cache不存在时,默认允许允许创建新的cache
boolean allowInFlightCacheCreation = true;
}
}
1.1.3.RedisCacheManager配置示例
RedisCacheConfiguration
key序列化器为StringRedisSerializer;
value序列化器为GenericJackson2JsonRedisSerializer;
缓存过期时间为60秒;
DefaultRedisCacheWriter
RedisCache
RedisCacheManager
没有初始化cache;
根据name获取cache,cache不存在时创建cache;
不支持事务;
@Bean
@ConditionalOnMissingBean(name = "cacheManager")
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//**redis默认配置文件,并且设置过期时间为60秒
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60));
//**设置序列化器
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));
//**创建RedisCacheManager生成器
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration);
return builder.build();
}
1.2.CompositeCacheManager
组合其它的cacheManager,可同时使用多种缓存;
问题:默认情况下getCache方法,当name对应的cache不存在时,会创建一个cache并返回。导致遍历的第一个cacheManager永远会返回cache,后面的cache就永远不会调用到,所以CompositeCacheManager管理的cacheManager必须初始化cache并禁止动态创建cache?;
public class CompositeCacheManager implements CacheManager, InitializingBean {
//**管理的其它CacheManager
private final List cacheManagers = new ArrayList<>();
//**是否在末尾添加一个NoOpCacheManager
private boolean fallbackToNoOpCache = false;
//**在末尾添加一个NoOpCacheManager,调用getCache匹配不到CacheManager的都会返回NoOpCacheManager
public void afterPropertiesSet() {
if (this.fallbackToNoOpCache) {
this.cacheManagers.add(new NoOpCacheManager());
}
}
//**根据name获取cache(问题:默认情况下getCache方法,当name对应的cache不存在时,会创建一个cache并返回
//**导致遍历的第一个cacheManager永远会返回cache,后面的cache就永远不会调用到)
public Cache getCache(String name) {
for (CacheManager cacheManager : this.cacheManagers) {
Cache cache = cacheManager.getCache(name);
if (cache != null) {
return cache;
}
}
return null;
}
//**获取所有的name
public Collection getCacheNames() {
Set names = new LinkedHashSet<>();
for (CacheManager manager : this.cacheManagers) {
names.addAll(manager.getCacheNames());
}
return Collections.unmodifiableSet(names);
}
}