Polish.
Additionally, refactors and cleans up the Redis cache extension and infrastructure. Closes #2583
This commit is contained in:
@@ -18,11 +18,12 @@ package org.springframework.data.redis.cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link CacheKeyPrefix} provides a hook for creating custom prefixes prepended to the actual {@literal key} stored in
|
||||
* Redis.
|
||||
* {@link CacheKeyPrefix} is a callback hook for creating custom prefixes prepended to the actual {@literal key}
|
||||
* stored in Redis.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @since 2.0.4
|
||||
*/
|
||||
@FunctionalInterface
|
||||
@@ -36,16 +37,20 @@ public interface CacheKeyPrefix {
|
||||
String SEPARATOR = "::";
|
||||
|
||||
/**
|
||||
* Compute the prefix for the actual {@literal key} stored in Redis.
|
||||
* Compute the {@link String prefix} for the actual {@literal cache key} stored in Redis.
|
||||
*
|
||||
* @param cacheName will never be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @param cacheName {@link String name} of the cache in which the key is stored;
|
||||
* will never be {@literal null}.
|
||||
* @return the computed {@link String prefix} of the {@literal cache key} stored in Redis;
|
||||
* never {@literal null}.
|
||||
*/
|
||||
String compute(String cacheName);
|
||||
|
||||
/**
|
||||
* Creates a default {@link CacheKeyPrefix} scheme that prefixes cache keys with {@code cacheName} followed by double
|
||||
* colons. A cache named {@code myCache} will prefix all cache keys with {@code myCache::}.
|
||||
* Creates a default {@link CacheKeyPrefix} scheme that prefixes cache keys with the {@link String name}
|
||||
* of the cache followed by double colons.
|
||||
*
|
||||
* For example, a cache named {@literal myCache} will prefix all cache keys with {@literal myCache::}.
|
||||
*
|
||||
* @return the default {@link CacheKeyPrefix} scheme.
|
||||
*/
|
||||
@@ -54,9 +59,12 @@ public interface CacheKeyPrefix {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link CacheKeyPrefix} scheme that prefixes cache keys with the given {@code prefix}. The prefix is
|
||||
* prepended to the {@code cacheName} followed by double colons. A prefix {@code redis-} with a cache named
|
||||
* {@code myCache} results in {@code redis-myCache::}.
|
||||
* Creates a {@link CacheKeyPrefix} scheme that prefixes cache keys with the given {@link String prefix}.
|
||||
*
|
||||
* The {@link String prefix} is prepended to the {@link String cacheName} followed by double colons.
|
||||
*
|
||||
* For example, a prefix {@literal redis-} with a cache named {@literal myCache}
|
||||
* results in {@literal redis-myCache::}.
|
||||
*
|
||||
* @param prefix must not be {@literal null}.
|
||||
* @return the default {@link CacheKeyPrefix} scheme.
|
||||
@@ -65,6 +73,7 @@ public interface CacheKeyPrefix {
|
||||
static CacheKeyPrefix prefixed(String prefix) {
|
||||
|
||||
Assert.notNull(prefix, "Prefix must not be null");
|
||||
|
||||
return name -> prefix + name + SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,14 @@ import java.util.Map.Entry;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.AbstractValueAdaptingCache;
|
||||
import org.springframework.cache.support.NullValue;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.util.ByteUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -38,63 +40,73 @@ import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cache.Cache} implementation using for Redis as underlying store.
|
||||
* <p>
|
||||
* {@link org.springframework.cache.Cache} implementation using for Redis as the underlying store for cache data.
|
||||
*
|
||||
* Use {@link RedisCacheManager} to create {@link RedisCache} instances.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Piotr Mionskowski
|
||||
* @author Jos Roseboom
|
||||
* @see RedisCacheConfiguration
|
||||
* @see RedisCacheWriter
|
||||
* @author John Blum
|
||||
* @see org.springframework.cache.support.AbstractValueAdaptingCache
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RedisCache extends AbstractValueAdaptingCache {
|
||||
|
||||
private static final byte[] BINARY_NULL_VALUE = RedisSerializer.java().serialize(NullValue.INSTANCE);
|
||||
|
||||
private final String name;
|
||||
private final RedisCacheConfiguration cacheConfiguration;
|
||||
|
||||
private final RedisCacheWriter cacheWriter;
|
||||
private final RedisCacheConfiguration cacheConfig;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Create new {@link RedisCache}.
|
||||
* Create a new {@link RedisCache}.
|
||||
*
|
||||
* @param name must not be {@literal null}.
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param cacheConfig must not be {@literal null}.
|
||||
* @param name {@link String name} for this {@link Cache}; must not be {@literal null}.
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param cacheConfiguration {@link RedisCacheConfiguration} applied to this {@link RedisCache on creation;
|
||||
* must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null} or the given {@link String} name for this {@link RedisCache} is {@literal null}.
|
||||
*/
|
||||
protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
|
||||
protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfiguration) {
|
||||
|
||||
super(cacheConfig.getAllowCacheNullValues());
|
||||
super(cacheConfiguration.getAllowCacheNullValues());
|
||||
|
||||
Assert.notNull(name, "Name must not be null");
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null");
|
||||
Assert.notNull(cacheConfig, "CacheConfig must not be null");
|
||||
Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null");
|
||||
|
||||
this.name = name;
|
||||
this.cacheWriter = cacheWriter;
|
||||
this.cacheConfig = cacheConfig;
|
||||
this.conversionService = cacheConfig.getConversionService();
|
||||
this.cacheConfiguration = cacheConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object lookup(Object key) {
|
||||
|
||||
byte[] value = cacheWriter.get(name, createAndConvertCacheKey(key));
|
||||
/**
|
||||
* Get {@link RedisCacheConfiguration} used.
|
||||
*
|
||||
* @return immutable {@link RedisCacheConfiguration}. Never {@literal null}.
|
||||
*/
|
||||
public RedisCacheConfiguration getCacheConfiguration() {
|
||||
return this.cacheConfiguration;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
protected RedisCacheWriter getCacheWriter() {
|
||||
return this.cacheWriter;
|
||||
}
|
||||
|
||||
return deserializeCacheValue(value);
|
||||
protected ConversionService getConversionService() {
|
||||
return getCacheConfiguration().getConversionService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,38 +114,59 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
return this.cacheWriter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheStatistics} snapshot for this cache instance. Statistics are accumulated per cache instance
|
||||
* and not from the backing Redis data store.
|
||||
*
|
||||
* @return statistics object for this {@link RedisCache}.
|
||||
* @since 2.4
|
||||
*/
|
||||
public CacheStatistics getStatistics() {
|
||||
return getCacheWriter().getCacheStatistics(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
|
||||
ValueWrapper result = get(key);
|
||||
|
||||
if (result != null) {
|
||||
return (T) result.get();
|
||||
}
|
||||
|
||||
return getSynchronized(key, valueLoader);
|
||||
return result != null ? (T) result.get()
|
||||
: getSynchronized(key, valueLoader);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized <T> T getSynchronized(Object key, Callable<T> valueLoader) {
|
||||
private synchronized @Nullable <T> T getSynchronized(Object key, Callable<T> valueLoader) {
|
||||
|
||||
ValueWrapper result = get(key);
|
||||
|
||||
if (result != null) {
|
||||
return (T) result.get();
|
||||
}
|
||||
return result != null ? (T) result.get()
|
||||
: loadCacheValue(key, valueLoader);
|
||||
}
|
||||
|
||||
protected <T> T loadCacheValue(Object key, Callable<T> valueLoader) {
|
||||
|
||||
T value;
|
||||
|
||||
try {
|
||||
value = valueLoader.call();
|
||||
} catch (Exception e) {
|
||||
throw new ValueRetrievalException(key, valueLoader, e);
|
||||
} catch (Exception cause) {
|
||||
throw new ValueRetrievalException(key, valueLoader, cause);
|
||||
}
|
||||
|
||||
put(key, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object lookup(Object key) {
|
||||
|
||||
byte[] value = getCacheWriter().get(getName(), createAndConvertCacheKey(key));
|
||||
|
||||
return value != null ? deserializeCacheValue(value) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object key, @Nullable Object value) {
|
||||
|
||||
@@ -141,12 +174,16 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
|
||||
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));
|
||||
String message = 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",
|
||||
getName());
|
||||
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
|
||||
getCacheWriter().put(getName(), createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
|
||||
getCacheConfiguration().getTtl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,19 +195,10 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
return get(key);
|
||||
}
|
||||
|
||||
byte[] result = cacheWriter.putIfAbsent(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue),
|
||||
cacheConfig.getTtl());
|
||||
byte[] result = getCacheWriter().putIfAbsent(getName(), createAndConvertCacheKey(key),
|
||||
serializeCacheValue(cacheValue), getCacheConfiguration().getTtl());
|
||||
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evict(Object key) {
|
||||
cacheWriter.remove(name, createAndConvertCacheKey(key));
|
||||
return result != null ? new SimpleValueWrapper(fromStoreValue(deserializeCacheValue(result))) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,26 +207,15 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear keys that match the provided {@code keyPattern}.
|
||||
* Clear keys that match the provided {@link String keyPattern}.
|
||||
* <p>
|
||||
* Useful when cache keys are formatted in a style where Redis patterns can be used for matching these.
|
||||
*
|
||||
* @param keyPattern the pattern of the key
|
||||
* @param keyPattern {@link String pattern} used to match Redis keys to clear.
|
||||
* @since 3.0
|
||||
*/
|
||||
public void clear(String keyPattern) {
|
||||
cacheWriter.clean(name, createAndConvertCacheKey(keyPattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheStatistics} snapshot for this cache instance. Statistics are accumulated per cache instance
|
||||
* and not from the backing Redis data store.
|
||||
*
|
||||
* @return statistics object for this {@link RedisCache}.
|
||||
* @since 2.4
|
||||
*/
|
||||
public CacheStatistics getStatistics() {
|
||||
return cacheWriter.getCacheStatistics(getName());
|
||||
getCacheWriter().clean(getName(), createAndConvertCacheKey(keyPattern));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,16 +224,12 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
* @since 2.4
|
||||
*/
|
||||
public void clearStatistics() {
|
||||
cacheWriter.clearStatistics(getName());
|
||||
getCacheWriter().clearStatistics(getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get {@link RedisCacheConfiguration} used.
|
||||
*
|
||||
* @return immutable {@link RedisCacheConfiguration}. Never {@literal null}.
|
||||
*/
|
||||
public RedisCacheConfiguration getCacheConfiguration() {
|
||||
return cacheConfig;
|
||||
@Override
|
||||
public void evict(Object key) {
|
||||
getCacheWriter().remove(getName(), createAndConvertCacheKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,28 +242,28 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
@Nullable
|
||||
protected Object preProcessCacheValue(@Nullable Object value) {
|
||||
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return isAllowNullValues() ? NullValue.INSTANCE : null;
|
||||
return value != null ? value
|
||||
: isAllowNullValues() ? NullValue.INSTANCE
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the key.
|
||||
* Serialize the given {@link String cache key}.
|
||||
*
|
||||
* @param cacheKey must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @param cacheKey {@link String cache key} to serialize; must not be {@literal null}.
|
||||
* @return an array of bytes from the given, serialized {@link String cache key}; never {@literal null}.
|
||||
* @see RedisCacheConfiguration#getKeySerializationPair()
|
||||
*/
|
||||
protected byte[] serializeCacheKey(String cacheKey) {
|
||||
return ByteUtils.getBytes(cacheConfig.getKeySerializationPair().write(cacheKey));
|
||||
return ByteUtils.getBytes(getCacheConfiguration().getKeySerializationPair().write(cacheKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the value to cache.
|
||||
* Serialize the {@link Object value} to cache as an array of bytes.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @param value {@link Object} to serialize and cache; must not be {@literal null}.
|
||||
* @return an array of bytes from the serialized {@link Object value}; never {@literal null}.
|
||||
* @see RedisCacheConfiguration#getValueSerializationPair()
|
||||
*/
|
||||
protected byte[] serializeCacheValue(Object value) {
|
||||
|
||||
@@ -258,14 +271,16 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
return BINARY_NULL_VALUE;
|
||||
}
|
||||
|
||||
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
|
||||
return ByteUtils.getBytes(getCacheConfiguration().getValueSerializationPair().write(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize the given value to the actual cache value.
|
||||
* Deserialize the given the array of bytes to the actual {@link Object cache value}.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return can be {@literal null}.
|
||||
* @param value array of bytes to deserialize; must not be {@literal null}.
|
||||
* @return an {@link Object} deserialized from the array of bytes using the configured value
|
||||
* {@link RedisSerializationContext.SerializationPair}; can be {@literal null}.
|
||||
* @see RedisCacheConfiguration#getValueSerializationPair()
|
||||
*/
|
||||
@Nullable
|
||||
protected Object deserializeCacheValue(byte[] value) {
|
||||
@@ -274,7 +289,7 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
return NullValue.INSTANCE;
|
||||
}
|
||||
|
||||
return cacheConfig.getValueSerializationPair().read(ByteBuffer.wrap(value));
|
||||
return getCacheConfiguration().getValueSerializationPair().read(ByteBuffer.wrap(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,7 +302,7 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
|
||||
String convertedKey = convertKey(key);
|
||||
|
||||
return cacheConfig.usePrefix() ? prefixCacheKey(convertedKey) : convertedKey;
|
||||
return getCacheConfiguration().usePrefix() ? prefixCacheKey(convertedKey) : convertedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,61 +320,79 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
|
||||
TypeDescriptor source = TypeDescriptor.forObject(key);
|
||||
|
||||
ConversionService conversionService = getConversionService();
|
||||
|
||||
if (conversionService.canConvert(source, TypeDescriptor.valueOf(String.class))) {
|
||||
try {
|
||||
return conversionService.convert(key, String.class);
|
||||
} catch (ConversionFailedException e) {
|
||||
} catch (ConversionFailedException cause) {
|
||||
|
||||
// may fail if the given key is a collection
|
||||
// May fail if the given key is a collection
|
||||
if (isCollectionLikeOrMap(source)) {
|
||||
return convertCollectionLikeOrMapKey(key, source);
|
||||
}
|
||||
|
||||
throw e;
|
||||
throw cause;
|
||||
}
|
||||
}
|
||||
|
||||
Method toString = ReflectionUtils.findMethod(key.getClass(), "toString");
|
||||
|
||||
if (toString != null && !Object.class.equals(toString.getDeclaringClass())) {
|
||||
if (hasToStringMethod(key)) {
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
throw new IllegalStateException(String.format(
|
||||
"Cannot convert cache key %s to String; Please register a suitable Converter via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'",
|
||||
source, key.getClass().getSimpleName()));
|
||||
String message = String.format("Cannot convert cache key %s to String; Please register a suitable Converter"
|
||||
+ " via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'",
|
||||
source, key.getClass().getName());
|
||||
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
|
||||
private boolean hasToStringMethod(Object target) {
|
||||
return hasToStringMethod(target.getClass());
|
||||
}
|
||||
|
||||
private boolean hasToStringMethod(Class<?> type) {
|
||||
|
||||
Method toString = ReflectionUtils.findMethod(type, "toString");
|
||||
|
||||
return toString != null && !Object.class.equals(toString.getDeclaringClass());
|
||||
}
|
||||
|
||||
private boolean isCollectionLikeOrMap(TypeDescriptor source) {
|
||||
return source.isArray() || source.isCollection() || source.isMap();
|
||||
}
|
||||
|
||||
private String convertCollectionLikeOrMapKey(Object key, TypeDescriptor source) {
|
||||
|
||||
if (source.isMap()) {
|
||||
|
||||
int count = 0;
|
||||
|
||||
StringBuilder target = new StringBuilder("{");
|
||||
|
||||
for (Entry<?, ?> entry : ((Map<?, ?>) key).entrySet()) {
|
||||
target.append(convertKey(entry.getKey())).append("=").append(convertKey(entry.getValue()));
|
||||
target.append(++count > 1 ? ", " : "");
|
||||
}
|
||||
|
||||
target.append("}");
|
||||
|
||||
return target.toString();
|
||||
} else if (source.isCollection() || source.isArray()) {
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
StringJoiner stringJoiner = new StringJoiner(",");
|
||||
|
||||
Collection<?> collection = source.isCollection() ? (Collection<?>) key
|
||||
: Arrays.asList(ObjectUtils.toObjectArray(key));
|
||||
|
||||
for (Object val : collection) {
|
||||
sj.add(convertKey(val));
|
||||
for (Object collectedKey : collection) {
|
||||
stringJoiner.add(convertKey(collectedKey));
|
||||
}
|
||||
return "[" + sj.toString() + "]";
|
||||
|
||||
return "[" + stringJoiner + "]";
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Cannot convert cache key %s to String", key));
|
||||
}
|
||||
|
||||
private boolean isCollectionLikeOrMap(TypeDescriptor source) {
|
||||
return source.isArray() || source.isCollection() || source.isMap();
|
||||
throw new IllegalArgumentException(String.format("Cannot convert cache key [%s] to String", key));
|
||||
}
|
||||
|
||||
private byte[] createAndConvertCacheKey(Object key) {
|
||||
@@ -367,9 +400,7 @@ public class RedisCache extends AbstractValueAdaptingCache {
|
||||
}
|
||||
|
||||
private String prefixCacheKey(String key) {
|
||||
|
||||
// allow contextual cache names by computing the key prefix on every call.
|
||||
return cacheConfig.getKeyPrefixFor(name) + key;
|
||||
return getCacheConfiguration().getKeyPrefixFor(getName()) + key;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,40 +31,23 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Immutable {@link RedisCacheConfiguration} helps customizing {@link RedisCache} behaviour such as caching
|
||||
* {@literal null} values, cache key prefixes and binary serialization. <br />
|
||||
* Start with {@link RedisCacheConfiguration#defaultCacheConfig()} and customize {@link RedisCache} behaviour from there
|
||||
* on.
|
||||
* Immutable {@link RedisCacheConfiguration} used to customize {@link RedisCache} behaviour, such as caching
|
||||
* {@literal null} values, computing cache key prefixes and handling binary serialization.
|
||||
*
|
||||
* Start with {@link RedisCacheConfiguration#defaultCacheConfig()} and customize {@link RedisCache} behaviour
|
||||
* from that point on.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @since 2.0
|
||||
*/
|
||||
public class RedisCacheConfiguration {
|
||||
|
||||
private final Duration ttl;
|
||||
private final boolean cacheNullValues;
|
||||
private final CacheKeyPrefix keyPrefix;
|
||||
private final boolean usePrefix;
|
||||
|
||||
private final SerializationPair<String> keySerializationPair;
|
||||
private final SerializationPair<Object> valueSerializationPair;
|
||||
|
||||
private final ConversionService conversionService;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
|
||||
SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this.ttl = ttl;
|
||||
this.cacheNullValues = cacheNullValues;
|
||||
this.usePrefix = usePrefix;
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.keySerializationPair = keySerializationPair;
|
||||
this.valueSerializationPair = (SerializationPair<Object>) valueSerializationPair;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
protected static final boolean DEFAULT_CACHE_NULL_VALUES = true;
|
||||
protected static final boolean DEFAULT_USE_PREFIX = true;
|
||||
protected static final boolean DO_NOT_CACHE_NULL_VALUES = false;
|
||||
protected static final boolean DO_NOT_USE_PREFIX = false;
|
||||
|
||||
/**
|
||||
* Default {@link RedisCacheConfiguration} using the following:
|
||||
@@ -123,23 +106,36 @@ public class RedisCacheConfiguration {
|
||||
|
||||
registerDefaultConverters(conversionService);
|
||||
|
||||
return new RedisCacheConfiguration(Duration.ZERO, true, true, CacheKeyPrefix.simple(),
|
||||
return new RedisCacheConfiguration(Duration.ZERO, DEFAULT_CACHE_NULL_VALUES, DEFAULT_USE_PREFIX,
|
||||
CacheKeyPrefix.simple(),
|
||||
SerializationPair.fromSerializer(RedisSerializer.string()),
|
||||
SerializationPair.fromSerializer(RedisSerializer.java(classLoader)), conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
|
||||
*
|
||||
* @param ttl must not be {@literal null}.
|
||||
* @return new {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheConfiguration entryTtl(Duration ttl) {
|
||||
private final boolean cacheNullValues;
|
||||
private final boolean usePrefix;
|
||||
|
||||
Assert.notNull(ttl, "TTL duration must not be null");
|
||||
private final CacheKeyPrefix keyPrefix;
|
||||
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
private final ConversionService conversionService;
|
||||
|
||||
private final Duration ttl;
|
||||
|
||||
private final SerializationPair<String> keySerializationPair;
|
||||
private final SerializationPair<Object> valueSerializationPair;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private RedisCacheConfiguration(Duration ttl, Boolean cacheNullValues, Boolean usePrefix, CacheKeyPrefix keyPrefix,
|
||||
SerializationPair<String> keySerializationPair, SerializationPair<?> valueSerializationPair,
|
||||
ConversionService conversionService) {
|
||||
|
||||
this.ttl = ttl;
|
||||
this.cacheNullValues = cacheNullValues;
|
||||
this.usePrefix = usePrefix;
|
||||
this.keyPrefix = keyPrefix;
|
||||
this.keySerializationPair = keySerializationPair;
|
||||
this.valueSerializationPair = (SerializationPair<Object>) valueSerializationPair;
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,8 +165,8 @@ public class RedisCacheConfiguration {
|
||||
|
||||
Assert.notNull(cacheKeyPrefix, "Function for computing prefix must not be null");
|
||||
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, true, cacheKeyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, DEFAULT_USE_PREFIX, cacheKeyPrefix,
|
||||
keySerializationPair, valueSerializationPair, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,8 +178,8 @@ public class RedisCacheConfiguration {
|
||||
* @return new {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheConfiguration disableCachingNullValues() {
|
||||
return new RedisCacheConfiguration(ttl, false, usePrefix, keyPrefix, keySerializationPair, valueSerializationPair,
|
||||
conversionService);
|
||||
return new RedisCacheConfiguration(ttl, DO_NOT_CACHE_NULL_VALUES, usePrefix, keyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,22 +191,22 @@ public class RedisCacheConfiguration {
|
||||
*/
|
||||
public RedisCacheConfiguration disableKeyPrefix() {
|
||||
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, false, keyPrefix, keySerializationPair,
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, DO_NOT_USE_PREFIX, keyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the {@link ConversionService} used for cache key to {@link String} conversion.
|
||||
* Set the ttl to apply for cache entries. Use {@link Duration#ZERO} to declare an eternal cache.
|
||||
*
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @param ttl must not be {@literal null}.
|
||||
* @return new {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheConfiguration withConversionService(ConversionService conversionService) {
|
||||
public RedisCacheConfiguration entryTtl(Duration ttl) {
|
||||
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
Assert.notNull(ttl, "TTL duration must not be null");
|
||||
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
valueSerializationPair, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,16 +238,24 @@ public class RedisCacheConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the computed {@literal key} prefix for a given {@literal cacheName}.
|
||||
* Define the {@link ConversionService} used for cache key to {@link String} conversion.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.0.4
|
||||
* @param conversionService must not be {@literal null}.
|
||||
* @return new {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public String getKeyPrefixFor(String cacheName) {
|
||||
public RedisCacheConfiguration withConversionService(ConversionService conversionService) {
|
||||
|
||||
Assert.notNull(cacheName, "Cache name must not be null");
|
||||
Assert.notNull(conversionService, "ConversionService must not be null");
|
||||
|
||||
return keyPrefix.compute(cacheName);
|
||||
return new RedisCacheConfiguration(ttl, cacheNullValues, usePrefix, keyPrefix, keySerializationPair,
|
||||
valueSerializationPair, conversionService);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if caching {@literal null} is allowed.
|
||||
*/
|
||||
public boolean getAllowCacheNullValues() {
|
||||
return cacheNullValues;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,10 +267,23 @@ public class RedisCacheConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} if caching {@literal null} is allowed.
|
||||
* @return The {@link ConversionService} used for cache key to {@link String} conversion. Never {@literal null}.
|
||||
*/
|
||||
public boolean getAllowCacheNullValues() {
|
||||
return cacheNullValues;
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the computed {@literal key} prefix for a given {@literal cacheName}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public String getKeyPrefixFor(String cacheName) {
|
||||
|
||||
Assert.notNull(cacheName, "Cache name must not be null");
|
||||
|
||||
return keyPrefix.compute(cacheName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,18 +308,12 @@ public class RedisCacheConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link ConversionService} used for cache key to {@link String} conversion. Never {@literal null}.
|
||||
*/
|
||||
public ConversionService getConversionService() {
|
||||
return conversionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Converter} for extracting the {@link String} representation of a cache key if no suitable
|
||||
* {@link Object#toString()} method is present.
|
||||
* Adds a {@link Converter} to extract the {@link String} representation of a {@literal cache key}
|
||||
* if no suitable {@link Object#toString()} method is present.
|
||||
*
|
||||
* @param cacheKeyConverter
|
||||
* @throws IllegalStateException if {@link #getConversionService()} does not allow converter registration.
|
||||
* @param cacheKeyConverter {@link Converter} used to convert a {@literal cache key} into a {@link String}.
|
||||
* @throws IllegalStateException if {@link #getConversionService()} does not allow {@link Converter} registration.
|
||||
* @see org.springframework.core.convert.converter.Converter
|
||||
* @since 2.2
|
||||
*/
|
||||
public void addCacheKeyConverter(Converter<?, String> cacheKeyConverter) {
|
||||
@@ -310,32 +321,40 @@ public class RedisCacheConfiguration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the underlying conversion system used to extract the cache key.
|
||||
* Configure the underlying {@link ConversionService} used to extract the {@literal cache key}.
|
||||
*
|
||||
* @param registryConsumer never {@literal null}.
|
||||
* @throws IllegalStateException if {@link #getConversionService()} does not allow converter registration.
|
||||
* @param registryConsumer {@link Consumer} used to register a {@link Converter}
|
||||
* with the configured {@link ConverterRegistry}; never {@literal null}.
|
||||
* @throws IllegalStateException if {@link #getConversionService()} does not allow {@link Converter} registration.
|
||||
* @see org.springframework.core.convert.converter.ConverterRegistry
|
||||
* @since 2.2
|
||||
*/
|
||||
public void configureKeyConverters(Consumer<ConverterRegistry> registryConsumer) {
|
||||
|
||||
if (!(getConversionService() instanceof ConverterRegistry)) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"'%s' returned by getConversionService() does not allow converter registration;" //
|
||||
+ " Please make sure to provide a ConversionService that implements ConverterRegistry",
|
||||
getConversionService().getClass().getSimpleName()));
|
||||
|
||||
String message = "'%s' returned by getConversionService() does not allow Converter registration;"
|
||||
+ " Please make sure to provide a ConversionService that implements ConverterRegistry";
|
||||
|
||||
throw new IllegalStateException(String.format(message, getConversionService().getClass().getName()));
|
||||
}
|
||||
|
||||
registryConsumer.accept((ConverterRegistry) getConversionService());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers default cache key converters. The following converters get registered:
|
||||
* Registers default cache {@link Converter key converters}.
|
||||
*
|
||||
* The following converters get registered:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link String} to {@link byte byte[]} using UTF-8 encoding.</li>
|
||||
* <li>{@link SimpleKey} to {@link String}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param registry must not be {@literal null}.
|
||||
* @param registry {@link ConverterRegistry} in which the {@link Converter key converters} are registered;
|
||||
* must not be {@literal null}.
|
||||
* @see org.springframework.core.convert.converter.ConverterRegistry
|
||||
*/
|
||||
public static void registerDefaultConverters(ConverterRegistry registry) {
|
||||
|
||||
|
||||
@@ -19,154 +19,90 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.util.RedisAssertions;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.cache.CacheManager} backed by a {@link RedisCache Redis} cache.
|
||||
* {@link CacheManager} backed by a {@link RedisCache}.
|
||||
* <p>
|
||||
* This cache manager creates caches by default upon first write. Empty caches are not visible on Redis due to how Redis
|
||||
* represents empty data structures.
|
||||
* This {@link CacheManager} creates {@link Cache caches} by default upon first write. Empty {@link Cache caches}
|
||||
* are not visible in Redis due to how Redis represents empty data structures.
|
||||
* <p>
|
||||
* Caches requiring a different {@link RedisCacheConfiguration} than the default configuration can be specified via
|
||||
* {@link RedisCacheManagerBuilder#withInitialCacheConfigurations(Map)}.
|
||||
* {@link Cache Caches} requiring a different {@link RedisCacheConfiguration} than the default cache configuration
|
||||
* can be specified via {@link RedisCacheManagerBuilder#withInitialCacheConfigurations(Map)} or individually
|
||||
* using {@link RedisCacheManagerBuilder#withCacheConfiguration(String, RedisCacheConfiguration)}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Yanming Zhou
|
||||
* @author John Blum
|
||||
* @see org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
* @since 2.0
|
||||
* @see RedisCacheConfiguration
|
||||
* @see RedisCacheWriter
|
||||
*/
|
||||
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
|
||||
|
||||
private final RedisCacheWriter cacheWriter;
|
||||
private final RedisCacheConfiguration defaultCacheConfig;
|
||||
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
|
||||
private final boolean allowInFlightCacheCreation;
|
||||
protected static final boolean DEFAULT_ALLOW_RUNTIME_CACHE_CREATION = true;
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
* Factory method returning a {@literal Builder} used to construct and configure a {@link RedisCacheManager}.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param allowInFlightCacheCreation allow create unconfigured caches.
|
||||
* @since 2.0.4
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @since 2.3
|
||||
*/
|
||||
private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
boolean allowInFlightCacheCreation) {
|
||||
public static RedisCacheManagerBuilder builder() {
|
||||
return new RedisCacheManagerBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method returning a {@literal Builder} used to construct and configure a {@link RedisCacheManager}
|
||||
* using the given {@link RedisCacheWriter}.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations by executing
|
||||
* appropriate Redis commands; must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @throws IllegalArgumentException if the given {@link RedisCacheWriter} is {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
*/
|
||||
public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
|
||||
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null");
|
||||
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null");
|
||||
|
||||
this.cacheWriter = cacheWriter;
|
||||
this.defaultCacheConfig = defaultCacheConfiguration;
|
||||
this.initialCacheConfiguration = new LinkedHashMap<>();
|
||||
this.allowInFlightCacheCreation = allowInFlightCacheCreation;
|
||||
return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
* Factory method returning a {@literal Builder} used to construct and configure a {@link RedisCacheManager}
|
||||
* using the given {@link RedisConnectionFactory}.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param connectionFactory {@link RedisConnectionFactory} used by the {@link RedisCacheManager}
|
||||
* to acquire connections to Redis when performing {@link RedisCache} operations; must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @throws IllegalArgumentException if the given {@link RedisConnectionFactory} is {@literal null}.
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
this(cacheWriter, defaultCacheConfiguration, true);
|
||||
public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
|
||||
return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
* Factory method used to construct a new {@link RedisCacheManager} using the given {@link RedisConnectionFactory}
|
||||
* with caching defaults applied.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param initialCacheNames optional set of known cache names that will be created with given
|
||||
* {@literal defaultCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
String... initialCacheNames) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, true, initialCacheNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param allowInFlightCacheCreation if set to {@literal true} no new caches can be acquire at runtime but limited to
|
||||
* the given list of initial cache names.
|
||||
* @param initialCacheNames optional set of known cache names that will be created with given
|
||||
* {@literal defaultCacheConfiguration}.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
boolean allowInFlightCacheCreation, String... initialCacheNames) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation);
|
||||
|
||||
for (String cacheName : initialCacheNames) {
|
||||
this.initialCacheConfiguration.put(cacheName, defaultCacheConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param initialCacheConfigurations Map of known cache names along with the configuration to use for those caches.
|
||||
* Must not be {@literal null}.
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration must not be {@literal null}. Maybe just use
|
||||
* {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param initialCacheConfigurations Map of known cache names along with the configuration to use for those caches.
|
||||
* Must not be {@literal null}.
|
||||
* @param allowInFlightCacheCreation if set to {@literal false} this cache manager is limited to the initial cache
|
||||
* configurations and will not create new caches at runtime.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation);
|
||||
|
||||
Assert.notNull(initialCacheConfigurations, "InitialCacheConfigurations must not be null");
|
||||
|
||||
this.initialCacheConfiguration.putAll(initialCacheConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link RedisCacheManager} with defaults applied.
|
||||
* <dl>
|
||||
* <dt>locking</dt>
|
||||
* <dd>disabled</dd>
|
||||
@@ -182,113 +118,330 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
* <dd>enabled</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @param connectionFactory must not be {@literal null}.
|
||||
* @return new instance of {@link RedisCacheManager}.
|
||||
* @param connectionFactory {@link RedisConnectionFactory} used by the {@link RedisCacheManager}
|
||||
* to acquire connections to Redis when performing {@link RedisCache} operations; must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManager}.
|
||||
* @throws IllegalArgumentException if the given {@link RedisConnectionFactory} is {@literal null}.
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
*/
|
||||
public static RedisCacheManager create(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
|
||||
return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory),
|
||||
RedisCacheConfiguration.defaultCacheConfig());
|
||||
RedisCacheConfiguration.defaultCacheConfig());
|
||||
}
|
||||
|
||||
private final boolean allowRuntimeCacheCreation;
|
||||
|
||||
private final RedisCacheConfiguration defaultCacheConfiguration;
|
||||
|
||||
private final RedisCacheWriter cacheWriter;
|
||||
|
||||
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter}
|
||||
* and a default {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* Allows cache creation at runtime.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
this(cacheWriter, defaultCacheConfiguration, DEFAULT_ALLOW_RUNTIME_CACHE_CREATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for builder style {@link RedisCacheManager} configuration.
|
||||
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter}
|
||||
* and a default {@link RedisCacheConfiguration}, and whether to allow cache creation at runtime.
|
||||
*
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @since 2.3
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @param allowRuntimeCacheCreation boolean to allow creation of undeclared caches at runtime;
|
||||
* {@literal true} by default. Maybe just use {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public static RedisCacheManagerBuilder builder() {
|
||||
return new RedisCacheManagerBuilder();
|
||||
private RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
boolean allowRuntimeCacheCreation) {
|
||||
|
||||
this.defaultCacheConfiguration = RedisAssertions.requireObject(defaultCacheConfiguration,
|
||||
"DefaultCacheConfiguration must not be null");
|
||||
|
||||
this.cacheWriter = RedisAssertions.requireObject(cacheWriter, "CacheWriter must not be null");
|
||||
this.initialCacheConfiguration = new LinkedHashMap<>();
|
||||
this.allowRuntimeCacheCreation = allowRuntimeCacheCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for builder style {@link RedisCacheManager} configuration.
|
||||
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter}
|
||||
* and a default {@link RedisCacheConfiguration}, along with an optional, initial set of {@link String cache names}
|
||||
* used to create {@link RedisCache Redis caches} on startup.
|
||||
*
|
||||
* @param connectionFactory must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* Allows cache creation at runtime.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @param initialCacheNames optional set of {@link String cache names} used to create {@link RedisCache Redis caches}
|
||||
* on startup. The default {@link RedisCacheConfiguration} will be applied to each cache.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
*/
|
||||
public static RedisCacheManagerBuilder builder(RedisConnectionFactory connectionFactory) {
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
String... initialCacheNames) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
|
||||
return RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory);
|
||||
this(cacheWriter, defaultCacheConfiguration, DEFAULT_ALLOW_RUNTIME_CACHE_CREATION, initialCacheNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for builder style {@link RedisCacheManager} configuration.
|
||||
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter}
|
||||
* and default {@link RedisCacheConfiguration}, and whether to allow cache creation at runtime.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* Additionally, the optional, initial set of {@link String cache names} witll be used to create
|
||||
* {@link RedisCache Redis caches} on startup.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @param allowRuntimeCacheCreation boolean to allow creation of undeclared caches at runtime;
|
||||
* {@literal true} by default. Maybe just use {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param initialCacheNames optional set of {@link String cache names} used to create {@link RedisCache Redis caches}
|
||||
* on startup. The default {@link RedisCacheConfiguration} will be applied to each cache.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public static RedisCacheManagerBuilder builder(RedisCacheWriter cacheWriter) {
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
boolean allowRuntimeCacheCreation, String... initialCacheNames) {
|
||||
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null");
|
||||
this(cacheWriter, defaultCacheConfiguration, allowRuntimeCacheCreation);
|
||||
|
||||
return RedisCacheManagerBuilder.fromCacheWriter(cacheWriter);
|
||||
for (String cacheName : initialCacheNames) {
|
||||
this.initialCacheConfiguration.put(cacheName, defaultCacheConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link RedisCacheManager} using given {@link RedisCacheWriter} and default
|
||||
* {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* Additionally, an initial {@link RedisCache} will be created and configured using the associated
|
||||
* {@link RedisCacheConfiguration} for each {@link String named} {@link RedisCache} in the given {@link Map}.
|
||||
*
|
||||
* Allows cache creation at runtime.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @param initialCacheConfigurations {@link Map} of declared, known {@link String cache names} along with associated
|
||||
* {@link RedisCacheConfiguration} used to create and configure {@link RedisCache Reds caches} on startup;
|
||||
* must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, DEFAULT_ALLOW_RUNTIME_CACHE_CREATION, initialCacheConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RedisCacheManager} initialized with the given {@link RedisCacheWriter}
|
||||
* and a default {@link RedisCacheConfiguration}, and whether to allow {@link RedisCache} creation at runtime.
|
||||
*
|
||||
* Additionally, an initial {@link RedisCache} will be created and configured using the associated
|
||||
* {@link RedisCacheConfiguration} for each {@link String named} {@link RedisCache} in the given {@link Map}.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations
|
||||
* by executing appropriate Redis commands; must not be {@literal null}.
|
||||
* @param defaultCacheConfiguration {@link RedisCacheConfiguration} applied to new {@link RedisCache Redis caches}
|
||||
* by default when no cache-specific {@link RedisCacheConfiguration} is provided; must not be {@literal null}.
|
||||
* @param allowRuntimeCacheCreation boolean to allow creation of undeclared caches at runtime;
|
||||
* {@literal true} by default. Maybe just use {@link RedisCacheConfiguration#defaultCacheConfig()}.
|
||||
* @param initialCacheConfigurations {@link Map} of declared, known {@link String cache names} along with associated
|
||||
* {@link RedisCacheConfiguration} used to create and configure {@link RedisCache Redis caches} on startup;
|
||||
* must not be {@literal null}.
|
||||
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
|
||||
* are {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheConfiguration
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
boolean allowRuntimeCacheCreation, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, allowRuntimeCacheCreation);
|
||||
|
||||
Assert.notNull(initialCacheConfigurations, "InitialCacheConfigurations must not be null");
|
||||
|
||||
this.initialCacheConfiguration.putAll(initialCacheConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link org.springframework.data.redis.cache.RedisCacheManager#RedisCacheManager(RedisCacheWriter, RedisCacheConfiguration, boolean, Map)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public RedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
|
||||
Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowRuntimeCacheCreation) {
|
||||
|
||||
this(cacheWriter, defaultCacheConfiguration, allowRuntimeCacheCreation, initialCacheConfigurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether {@link RedisCache Redis caches} are allowed to be created at runtime.
|
||||
*
|
||||
* @return a boolean value indicating whether {@link RedisCache Redis caches} are allowed to be created at runtime.
|
||||
*/
|
||||
public boolean isAllowRuntimeCacheCreation() {
|
||||
return this.allowRuntimeCacheCreation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link Collections#unmodifiableMap(Map) unmodifiable Map} containing {@link String caches name}
|
||||
* mapped to the {@link RedisCache} {@link RedisCacheConfiguration configuration}.
|
||||
*
|
||||
* @return unmodifiable {@link Map} containing {@link String cache name}
|
||||
* / {@link RedisCacheConfiguration configuration} pairs.
|
||||
*/
|
||||
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
|
||||
|
||||
Map<String, RedisCacheConfiguration> cacheConfigurationMap = new HashMap<>(getCacheNames().size());
|
||||
|
||||
getCacheNames().forEach(cacheName -> {
|
||||
RedisCache cache = (RedisCache) lookupCache(cacheName);
|
||||
cacheConfigurationMap.put(cacheName, cache != null ? cache.getCacheConfiguration() : null);
|
||||
});
|
||||
|
||||
return Collections.unmodifiableMap(cacheConfigurationMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default {@link RedisCacheConfiguration} applied to new {@link RedisCache} instances on creation
|
||||
* when custom, non-specific {@link RedisCacheConfiguration} was not provided.
|
||||
*
|
||||
* @return the default {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
protected RedisCacheConfiguration getDefaultCacheConfiguration() {
|
||||
return this.defaultCacheConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link Map} of {@link String cache names} to {@link RedisCacheConfiguration} objects as the initial set
|
||||
* of {@link RedisCache Redis caches} to create on startup.
|
||||
*
|
||||
* @return a {@link Map} of {@link String cache names} to {@link RedisCacheConfiguration} objects.
|
||||
*/
|
||||
protected Map<String, RedisCacheConfiguration> getInitialCacheConfiguration() {
|
||||
return Collections.unmodifiableMap(this.initialCacheConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisCache getMissingCache(String name) {
|
||||
return isAllowRuntimeCacheCreation() ? createRedisCache(name, getDefaultCacheConfiguration()) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RedisCache} with given {@link String name} and {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* @param name {@link String name} for the {@link RedisCache}; must not be {@literal null}.
|
||||
* @param cacheConfiguration {@link RedisCacheConfiguration} used to configure the {@link RedisCache};
|
||||
* resolves to the {@link #getDefaultCacheConfiguration()} if {@literal null}.
|
||||
* @return a new {@link RedisCache} instance; never {@literal null}.
|
||||
*/
|
||||
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfiguration) {
|
||||
return new RedisCache(name, cacheWriter, resolveCacheConfiguration(cacheConfiguration));
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
return getInitialCacheConfiguration().entrySet().stream()
|
||||
.map(entry -> createRedisCache(entry.getKey(), entry.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RedisCache getMissingCache(String name) {
|
||||
return allowInFlightCacheCreation ? createRedisCache(name, defaultCacheConfig) : null;
|
||||
private RedisCacheConfiguration resolveCacheConfiguration(@Nullable RedisCacheConfiguration cacheConfiguration) {
|
||||
return cacheConfiguration != null ? cacheConfiguration : getDefaultCacheConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return unmodifiable {@link Map} containing cache name / configuration pairs. Never {@literal null}.
|
||||
*/
|
||||
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
|
||||
|
||||
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
|
||||
|
||||
getCacheNames().forEach(it -> {
|
||||
|
||||
RedisCache cache = RedisCache.class.cast(lookupCache(it));
|
||||
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
|
||||
});
|
||||
|
||||
return Collections.unmodifiableMap(configurationMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration hook for creating {@link RedisCache} with given name and {@code cacheConfig}.
|
||||
*
|
||||
* @param name must not be {@literal null}.
|
||||
* @param cacheConfig can be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
|
||||
return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configurator for creating {@link RedisCacheManager}.
|
||||
* {@literal Builder} for creating a {@link RedisCacheManager}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @author Kezhu Wang
|
||||
* @author John Blum
|
||||
* @since 2.0
|
||||
*/
|
||||
public static class RedisCacheManagerBuilder {
|
||||
|
||||
private @Nullable RedisCacheWriter cacheWriter;
|
||||
private CacheStatisticsCollector statisticsCollector = CacheStatisticsCollector.none();
|
||||
private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
|
||||
/**
|
||||
* Factory method returning a new {@literal Builder} used to create and configure a {@link RedisCacheManager}
|
||||
* using the given {@link RedisCacheWriter}.
|
||||
*
|
||||
* @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations by executing
|
||||
* appropriate Redis commands; must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @throws IllegalArgumentException if the given {@link RedisCacheWriter} is {@literal null}.
|
||||
* @see org.springframework.data.redis.cache.RedisCacheWriter
|
||||
*/
|
||||
public static RedisCacheManagerBuilder fromCacheWriter(RedisCacheWriter cacheWriter) {
|
||||
return new RedisCacheManagerBuilder(RedisAssertions.requireObject(cacheWriter,
|
||||
"CacheWriter must not be null"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method returning a new {@literal Builder} used to create and configure a {@link RedisCacheManager}
|
||||
* using the given {@link RedisConnectionFactory}.
|
||||
*
|
||||
* @param connectionFactory {@link RedisConnectionFactory} used by the {@link RedisCacheManager}
|
||||
* to acquire connections to Redis when performing {@link RedisCache} operations; must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @throws IllegalArgumentException if the given {@link RedisConnectionFactory} is {@literal null}.
|
||||
* @see org.springframework.data.redis.connection.RedisConnectionFactory
|
||||
*/
|
||||
public static RedisCacheManagerBuilder fromConnectionFactory(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(
|
||||
RedisAssertions.requireObject(connectionFactory, "ConnectionFactory must not be null"));
|
||||
|
||||
return new RedisCacheManagerBuilder(cacheWriter);
|
||||
}
|
||||
|
||||
private boolean allowRuntimeCacheCreation = true;
|
||||
private boolean enableTransactions;
|
||||
boolean allowInFlightCacheCreation = true;
|
||||
|
||||
private CacheStatisticsCollector statisticsCollector = CacheStatisticsCollector.none();
|
||||
|
||||
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
|
||||
|
||||
private RedisCacheConfiguration defaultCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
|
||||
|
||||
private @Nullable RedisCacheWriter cacheWriter;
|
||||
|
||||
private RedisCacheManagerBuilder() {}
|
||||
|
||||
@@ -297,29 +450,52 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for builder style {@link RedisCacheManager} configuration.
|
||||
* Configure whether to allow cache creation at runtime.
|
||||
*
|
||||
* @param connectionFactory must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* @param allowRuntimeCacheCreation boolean to allow creation of undeclared caches at runtime;
|
||||
* {@literal true} by default.
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
*/
|
||||
public static RedisCacheManagerBuilder fromConnectionFactory(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
|
||||
return new RedisCacheManagerBuilder(RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory));
|
||||
public RedisCacheManagerBuilder allowCreateOnMissingCache(boolean allowRuntimeCacheCreation) {
|
||||
this.allowRuntimeCacheCreation = allowRuntimeCacheCreation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point for builder style {@link RedisCacheManager} configuration.
|
||||
* Disable {@link RedisCache} creation at runtime for unconfigured, undeclared caches.
|
||||
*
|
||||
* @param cacheWriter must not be {@literal null}.
|
||||
* @return new {@link RedisCacheManagerBuilder}.
|
||||
* {@link RedisCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured {@link Cache}
|
||||
* instead of a new {@link RedisCache} instance. This allows the
|
||||
* {@link org.springframework.cache.support.CompositeCacheManager} to participate.
|
||||
*
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
* @see #allowCreateOnMissingCache(boolean)
|
||||
* @see #enableCreateOnMissingCache()
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public static RedisCacheManagerBuilder fromCacheWriter(RedisCacheWriter cacheWriter) {
|
||||
public RedisCacheManagerBuilder disableCreateOnMissingCache() {
|
||||
return allowCreateOnMissingCache(false);
|
||||
}
|
||||
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null");
|
||||
/**
|
||||
* Enables {@link RedisCache} creation at runtime for unconfigured, undeclared caches.
|
||||
*
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
* @see #allowCreateOnMissingCache(boolean)
|
||||
* @see #disableCreateOnMissingCache()
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public RedisCacheManagerBuilder enableCreateOnMissingCache() {
|
||||
return allowCreateOnMissingCache(true);
|
||||
}
|
||||
|
||||
return new RedisCacheManagerBuilder(cacheWriter);
|
||||
/**
|
||||
* Returns the default {@link RedisCacheConfiguration}.
|
||||
*
|
||||
* @return the default {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheConfiguration cacheDefaults() {
|
||||
return this.defaultCacheConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,22 +506,12 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
*/
|
||||
public RedisCacheManagerBuilder cacheDefaults(RedisCacheConfiguration defaultCacheConfiguration) {
|
||||
|
||||
Assert.notNull(defaultCacheConfiguration, "DefaultCacheConfiguration must not be null");
|
||||
|
||||
this.defaultCacheConfiguration = defaultCacheConfiguration;
|
||||
this.defaultCacheConfiguration = RedisAssertions.requireObject(defaultCacheConfiguration,
|
||||
"DefaultCacheConfiguration must not be null");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns applied {@link RedisCacheConfiguration}, allow customization base on it.
|
||||
*
|
||||
* @return applied {@link RedisCacheConfiguration}.
|
||||
*/
|
||||
public RedisCacheConfiguration cacheDefaults() {
|
||||
return this.defaultCacheConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@link RedisCacheWriter}.
|
||||
*
|
||||
@@ -354,23 +520,17 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
* @since 2.3
|
||||
*/
|
||||
public RedisCacheManagerBuilder cacheWriter(RedisCacheWriter cacheWriter) {
|
||||
|
||||
Assert.notNull(cacheWriter, "CacheWriter must not be null");
|
||||
|
||||
this.cacheWriter = cacheWriter;
|
||||
|
||||
this.cacheWriter = RedisAssertions.requireObject(cacheWriter, "CacheWriter must not be null");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable {@link RedisCache}s to synchronize cache put/evict operations with ongoing Spring-managed transactions.
|
||||
* Enables cache statistics.
|
||||
*
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
*/
|
||||
public RedisCacheManagerBuilder transactionAware() {
|
||||
|
||||
this.enableTransactions = true;
|
||||
|
||||
public RedisCacheManagerBuilder enableStatistics() {
|
||||
this.statisticsCollector = CacheStatisticsCollector.create();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -384,9 +544,39 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
*/
|
||||
public RedisCacheManagerBuilder initialCacheNames(Set<String> cacheNames) {
|
||||
|
||||
Assert.notNull(cacheNames, "CacheNames must not be null");
|
||||
RedisAssertions.requireObject(cacheNames, "CacheNames must not be null")
|
||||
.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable {@link RedisCache}s to synchronize cache put/evict operations with ongoing Spring-managed transactions.
|
||||
*
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
*/
|
||||
public RedisCacheManagerBuilder transactionAware() {
|
||||
this.enableTransactions = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given {@link String cache name} and {@link RedisCacheConfiguration} used to
|
||||
* create and configure a {@link RedisCache} on startup.
|
||||
*
|
||||
* @param cacheName {@link String name} of the cache to register for creation on startup.
|
||||
* @param cacheConfiguration {@link RedisCacheConfiguration} used to configure the new cache on startup.
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public RedisCacheManagerBuilder withCacheConfiguration(String cacheName,
|
||||
RedisCacheConfiguration cacheConfiguration) {
|
||||
|
||||
Assert.notNull(cacheName, "CacheName must not be null");
|
||||
Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null");
|
||||
|
||||
this.initialCaches.put(cacheName, cacheConfiguration);
|
||||
|
||||
cacheNames.forEach(it -> withCacheConfiguration(it, defaultCacheConfiguration));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -399,57 +589,15 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
public RedisCacheManagerBuilder withInitialCacheConfigurations(
|
||||
Map<String, RedisCacheConfiguration> cacheConfigurations) {
|
||||
|
||||
Assert.notNull(cacheConfigurations, "CacheConfigurations must not be null");
|
||||
cacheConfigurations.forEach((cacheName, configuration) -> Assert.notNull(configuration,
|
||||
String.format("RedisCacheConfiguration for cache %s must not be null", cacheName)));
|
||||
RedisAssertions.requireObject(cacheConfigurations, "CacheConfigurations must not be null")
|
||||
.forEach((cacheName, cacheConfiguration) -> RedisAssertions.requireObject(cacheConfiguration,
|
||||
"RedisCacheConfiguration for cache [%s] must not be null", cacheName));
|
||||
|
||||
this.initialCaches.putAll(cacheConfigurations);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cacheName
|
||||
* @param cacheConfiguration
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public RedisCacheManagerBuilder withCacheConfiguration(String cacheName,
|
||||
RedisCacheConfiguration cacheConfiguration) {
|
||||
|
||||
Assert.notNull(cacheName, "CacheName must not be null");
|
||||
Assert.notNull(cacheConfiguration, "CacheConfiguration must not be null");
|
||||
|
||||
this.initialCaches.put(cacheName, cacheConfiguration);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable in-flight {@link org.springframework.cache.Cache} creation for unconfigured caches.
|
||||
* <p>
|
||||
* {@link RedisCacheManager#getMissingCache(String)} returns {@literal null} for any unconfigured
|
||||
* {@link org.springframework.cache.Cache} instead of a new {@link RedisCache} instance. This allows eg.
|
||||
* {@link org.springframework.cache.support.CompositeCacheManager} to chime in.
|
||||
*
|
||||
* @return this {@link RedisCacheManagerBuilder}.
|
||||
* @since 2.0.4
|
||||
*/
|
||||
public RedisCacheManagerBuilder disableCreateOnMissingCache() {
|
||||
|
||||
this.allowInFlightCacheCreation = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Set} of cache names for which the builder holds {@link RedisCacheConfiguration configuration}.
|
||||
*
|
||||
* @return an unmodifiable {@link Set} holding the name of caches for which a {@link RedisCacheConfiguration
|
||||
* configuration} has been set.
|
||||
* @since 2.2
|
||||
*/
|
||||
public Set<String> getConfiguredCaches() {
|
||||
return Collections.unmodifiableSet(this.initialCaches.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link RedisCacheConfiguration} for a given cache by its name.
|
||||
*
|
||||
@@ -462,13 +610,14 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 2.4
|
||||
* Get the {@link Set} of cache names for which the builder holds {@link RedisCacheConfiguration configuration}.
|
||||
*
|
||||
* @return an unmodifiable {@link Set} holding the name of caches for which a {@link RedisCacheConfiguration
|
||||
* configuration} has been set.
|
||||
* @since 2.2
|
||||
*/
|
||||
public RedisCacheManagerBuilder enableStatistics() {
|
||||
|
||||
this.statisticsCollector = CacheStatisticsCollector.create();
|
||||
return this;
|
||||
public Set<String> getConfiguredCaches() {
|
||||
return Collections.unmodifiableSet(this.initialCaches.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -478,21 +627,22 @@ public class RedisCacheManager extends AbstractTransactionSupportingCacheManager
|
||||
*/
|
||||
public RedisCacheManager build() {
|
||||
|
||||
Assert.state(cacheWriter != null,
|
||||
"CacheWriter must not be null You can provide one via 'RedisCacheManagerBuilder#cacheWriter(RedisCacheWriter)'");
|
||||
Assert.state(cacheWriter != null, "CacheWriter must not be null;"
|
||||
+ " You can provide one via 'RedisCacheManagerBuilder#cacheWriter(RedisCacheWriter)'");
|
||||
|
||||
RedisCacheWriter theCacheWriter = cacheWriter;
|
||||
RedisCacheWriter resolvedCacheWriter = !CacheStatisticsCollector.none().equals(statisticsCollector)
|
||||
? cacheWriter.withStatisticsCollector(statisticsCollector)
|
||||
: cacheWriter;
|
||||
|
||||
if (!statisticsCollector.equals(CacheStatisticsCollector.none())) {
|
||||
theCacheWriter = cacheWriter.withStatisticsCollector(statisticsCollector);
|
||||
}
|
||||
RedisCacheManager cacheManager = newRedisCacheManager(resolvedCacheWriter);
|
||||
|
||||
RedisCacheManager cm = new RedisCacheManager(theCacheWriter, defaultCacheConfiguration, initialCaches,
|
||||
allowInFlightCacheCreation);
|
||||
cacheManager.setTransactionAware(enableTransactions);
|
||||
|
||||
cm.setTransactionAware(enableTransactions);
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
return cm;
|
||||
private RedisCacheManager newRedisCacheManager(RedisCacheWriter cacheWriter) {
|
||||
return new RedisCacheManager(cacheWriter, cacheDefaults(), allowRuntimeCacheCreation, initialCaches);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2017-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.redis.util;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract utility class for common assertions used in Spring Data Redis.
|
||||
*
|
||||
* @author John Blum
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public abstract class RedisAssertions {
|
||||
|
||||
/**
|
||||
* Asserts the given {@link Object} is not {@literal null}.
|
||||
*
|
||||
* @param <T> {@link Class type} of {@link Object} being asserted.
|
||||
* @param target {@link Object} to evaluate.
|
||||
* @param message {@link String} containing the message for the thrown exception.
|
||||
* @param arguments array of {@link Object} arguments used to format the {@link String message}.
|
||||
* @return the given {@link Object}.
|
||||
* @throws IllegalArgumentException if the {@link Object target} is {@literal null}.
|
||||
* @see #requireObject(Object, Supplier)
|
||||
*/
|
||||
public static <T> T requireObject(@Nullable T target, String message, Object... arguments) {
|
||||
return requireObject(target, () -> String.format(message, arguments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the given {@link Object} is not {@literal null}.
|
||||
*
|
||||
* @param <T> {@link Class type} of {@link Object} being asserted.
|
||||
* @param target {@link Object} to evaluate.
|
||||
* @param message {@link Supplier} supplying the message for the thrown exception.
|
||||
* @return the given {@link Object}.
|
||||
* @throws IllegalArgumentException if the {@link Object target} is {@literal null}.
|
||||
*/
|
||||
public static <T> T requireObject(@Nullable T target, Supplier<String> message) {
|
||||
Assert.notNull(target, message);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user