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> T execute(String name, Function<RedisConnection, T> callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
checkAndPotentiallyWaitUntilUnlocked(name, connection);
return callback.apply(connection);
} finally {
connection.close();
}
}
//**執行redis命令
private void executeLockFree(Consumer<RedisConnection> 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<String> keySerializationPair;
//**value的序列化器,持有RedisSerializer的引用,value可以是任意類型
private final SerializationPair<Object> 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<ConverterRegistry> 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> T get(Object key, Callable<T> 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<String, RedisCacheConfiguration> 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<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList<>();
for (Map.Entry<String, RedisCacheConfiguration> 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<String, RedisCacheConfiguration> 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<CacheManager> 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<String> getCacheNames() {
Set<String> names = new LinkedHashSet<>();
for (CacheManager manager : this.cacheManagers) {
names.addAll(manager.getCacheNames());
}
return Collections.unmodifiableSet(names);
}
}