springmvc 中有自带的cache处理模块,可以是方法级别的缓存处理,那么在实际使用中,很可能自己造轮子,因为实际中永远会有更奇怪的需求点。比如:

1 清除缓存时候,能模糊的进行删除

2 针对不同的key,设置不同的过期时间

这2个是有些麻烦的需求,当然针对缓存内容,设置 key(这个 key 的确定)更让人难受,不好取舍,需要有一定的开发经验,否则只能不停的修改。

我们先集中处理第一个问题,模糊删除

  • 查找方案
  • 查看低版本redis实现
  • 具体处理方式

明确问题,查找方案

可能网上有不少的解决方案

  1. 直接重写 https://blog.csdn.net/Crystalqy/article/details/110681684
  2. spring 5 + 版本的 https://my.oschina.net/u/220938/blog/3196609
  3. 具有启发性的 https://blog.csdn.net/yali_aini/article/details/89923548

1. 首先我们从网上找到对应的修改的code,真的就是拿来就能用的那种,然后发现有2个function没有,然后就发现你是低版本,然后就没然后了。。

    <properties>
<org.springframework-version>4.2.2.RELEASE</org.springframework-version>
<org.aspectj-version>1.8.2</org.aspectj-version>
<org.slf4j-version>1.7.21</org.slf4j-version>
<org.log4j2-version>2.8.2</org.log4j2-version>
</properties>

2. 根据第三个,可以看到,基于 redis template 的缓存处理,是有模糊处理的方法的,也就是说,可以做模糊处理。

3. 查看 spring 低版本 4.2.2 版本的 cache 的redis 类,进行简单的 仿做


查看低版本redis实现

因为使用springmvc时候,都会对 redis 进行配置,设置 ttl 等参数,那么,点进去看源码,就会发现

CustomizedRedisCacheManagerCustomizeRedisCache ,和 高版本的名字很像,那么仔细看看,发现 CustomizeRedisCache 就是需要改造的。

  public void evict(RedisCacheElement element)
public void evict(Object key)

这2个函数。很可以,2个文件粘贴出来,直接做成注入,发现就直接可以在 @Cacheable 的时候断点看了。

这2个就是在删除缓存时候使用的。


改造一波

CustomizedRedisCacheManager

 package oldmvc.config.cache;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.TransactionAwareCacheDecorator;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.cache.DefaultRedisCachePrefix;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCachePrefix;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; /**
* CustomizedRedisCacheManager
*
* @desc 重新定义 oldcache 的 处理方式
*/ public class CustomizedRedisCacheManager extends RedisCacheManager {
private final Log logger;
private final RedisOperations redisOperations;
private boolean usePrefix;
private RedisCachePrefix cachePrefix;
private boolean loadRemoteCachesOnStartup;
private boolean dynamic;
private long defaultExpiration;
private Map<String, Long> expires;
private Set<String> configuredCacheNames; public CustomizedRedisCacheManager(RedisOperations redisOperations) {
this(redisOperations, Collections.emptyList());
} public CustomizedRedisCacheManager(RedisOperations redisOperations, Collection<String> cacheNames) {
super(redisOperations, cacheNames); this.logger = LogFactory.getLog(CustomizedRedisCacheManager.class);
this.usePrefix = false;
this.cachePrefix = new DefaultRedisCachePrefix();
this.loadRemoteCachesOnStartup = false;
this.dynamic = true;
this.defaultExpiration = 0L;
this.expires = null;
this.redisOperations = redisOperations;
this.setCacheNames(cacheNames);
} public Cache getCache(String name) {
Cache cache = super.getCache(name);
return cache == null && this.dynamic ? this.createAndAddCache(name) : cache;
} public void setCacheNames(Collection<String> cacheNames) {
Set<String> newCacheNames = CollectionUtils.isEmpty(cacheNames) ? Collections.emptySet() : new HashSet(cacheNames);
this.configuredCacheNames = (Set) newCacheNames;
this.dynamic = ((Set) newCacheNames).isEmpty();
} public void setUsePrefix(boolean usePrefix) {
this.usePrefix = usePrefix;
} public void setCachePrefix(RedisCachePrefix cachePrefix) {
this.cachePrefix = cachePrefix;
} public void setDefaultExpiration(long defaultExpireTime) {
this.defaultExpiration = defaultExpireTime;
} public void setExpires(Map<String, Long> expires) {
this.expires = expires != null ? new ConcurrentHashMap(expires) : null;
} public void setLoadRemoteCachesOnStartup(boolean loadRemoteCachesOnStartup) {
this.loadRemoteCachesOnStartup = loadRemoteCachesOnStartup;
} protected Collection<? extends Cache> loadCaches() {
Assert.notNull(this.redisOperations, "A redis template is required in order to interact with data store");
return this.addConfiguredCachesIfNecessary(this.loadRemoteCachesOnStartup ? this.loadAndInitRemoteCaches() : Collections.emptyList());
} protected Collection<? extends Cache> addConfiguredCachesIfNecessary(Collection<? extends Cache> caches) {
Assert.notNull(caches, "Caches must not be null!");
Collection<Cache> result = new ArrayList(caches);
Iterator var3 = this.getCacheNames().iterator(); while (var3.hasNext()) {
String cacheName = (String) var3.next();
boolean configuredCacheAlreadyPresent = false;
Iterator var6 = caches.iterator(); while (var6.hasNext()) {
Cache cache = (Cache) var6.next();
if (cache.getName().equals(cacheName)) {
configuredCacheAlreadyPresent = true;
break;
}
} if (!configuredCacheAlreadyPresent) {
result.add(this.getCache(cacheName));
}
} return result;
} protected Cache createAndAddCache(String cacheName) {
this.addCache(this.createCache(cacheName));
return super.getCache(cacheName);
} protected RedisCache createCache(String cacheName) {
long expiration = this.computeExpiration(cacheName); return new CustomizeRedisCache(cacheName, this.usePrefix ? this.cachePrefix.prefix(cacheName) : null, this.redisOperations, expiration); // return new RedisCache(cacheName, this.usePrefix ? this.cachePrefix.prefix(cacheName) : null, this.redisOperations, expiration);
} protected long computeExpiration(String name) {
Long expiration = null;
if (this.expires != null) {
expiration = (Long) this.expires.get(name);
} return expiration != null ? expiration.longValue() : this.defaultExpiration;
} protected List<Cache> loadAndInitRemoteCaches() {
ArrayList caches = new ArrayList(); try {
Set<String> cacheNames = this.loadRemoteCacheKeys();
if (!CollectionUtils.isEmpty(cacheNames)) {
Iterator var3 = cacheNames.iterator(); while (var3.hasNext()) {
String cacheName = (String) var3.next();
if (null == super.getCache(cacheName)) {
caches.add(this.createCache(cacheName));
}
}
}
} catch (Exception var5) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Failed to initialize cache with remote cache keys.", var5);
}
} return caches;
} protected Set<String> loadRemoteCacheKeys() {
return (Set) this.redisOperations.execute(new RedisCallback<Set<String>>() {
public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {
Set<byte[]> keys = connection.keys(CustomizedRedisCacheManager.this.redisOperations.getKeySerializer().serialize("*~keys"));
Set<String> cacheKeys = new LinkedHashSet();
if (!CollectionUtils.isEmpty(keys)) {
Iterator var4 = keys.iterator(); while (var4.hasNext()) {
byte[] key = (byte[]) var4.next();
cacheKeys.add(CustomizedRedisCacheManager.this.redisOperations.getKeySerializer().deserialize(key).toString().replace("~keys", ""));
}
} return cacheKeys;
}
});
} protected RedisOperations getRedisOperations() {
return this.redisOperations;
} protected RedisCachePrefix getCachePrefix() {
return this.cachePrefix;
} protected boolean isUsePrefix() {
return this.usePrefix;
} public void afterPropertiesSet() {
if (!CollectionUtils.isEmpty(this.configuredCacheNames)) {
Iterator var1 = this.configuredCacheNames.iterator(); while (var1.hasNext()) {
String cacheName = (String) var1.next();
this.createAndAddCache(cacheName);
} this.configuredCacheNames.clear();
} super.afterPropertiesSet();
} protected Cache decorateCache(Cache cache) {
return this.isCacheAlreadyDecorated(cache) ? cache : super.decorateCache(cache);
} protected boolean isCacheAlreadyDecorated(Cache cache) {
return this.isTransactionAware() && cache instanceof TransactionAwareCacheDecorator;
}
}

CustomizeRedisCache


package oldmvc.config.cache;
import org.apache.commons.lang.StringUtils;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheElement;
import org.springframework.data.redis.cache.RedisCacheKey;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils; import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.Callable; /**
* RedisCacheResolver
*
* @desc springCache 的重载
*/
public class CustomizeRedisCache extends RedisCache {
private final RedisOperations redisOperations;
private final CustomizeRedisCache.RedisCacheMetadata cacheMetadata;
private final CacheValueAccessor cacheValueAccessor; public CustomizeRedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations, long expiration) {
super(name, prefix, redisOperations, expiration); Assert.hasText(name, "non-empty cache name is required");
this.cacheMetadata = new CustomizeRedisCache.RedisCacheMetadata(name, prefix);
this.cacheMetadata.setDefaultExpiration(expiration);
this.redisOperations = redisOperations;
this.cacheValueAccessor = new CustomizeRedisCache.CacheValueAccessor(redisOperations.getValueSerializer());
} public <T> T get(Object key, Class<T> type) {
ValueWrapper wrapper = this.get(key);
return wrapper == null ? null : (T) wrapper.get();
} public ValueWrapper get(Object key) {
return this.get((new RedisCacheKey(key)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()));
} public <T> T get(Object key, Callable<T> valueLoader) {
CustomizeRedisCache.BinaryRedisCacheElement rce = new CustomizeRedisCache.BinaryRedisCacheElement(new RedisCacheElement((new RedisCacheKey(key)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()), valueLoader), this.cacheValueAccessor);
ValueWrapper val = this.get(key);
if (val != null) {
return (T) val.get();
} else {
CustomizeRedisCache.RedisWriteThroughCallback callback = new CustomizeRedisCache.RedisWriteThroughCallback(rce, this.cacheMetadata); try {
byte[] result = (byte[]) ((byte[]) this.redisOperations.execute(callback));
return result == null ? null : (T) this.cacheValueAccessor.deserializeIfNecessary(result);
} catch (RuntimeException var7) {
throw CustomizeRedisCache.CacheValueRetrievalExceptionFactory.INSTANCE.create(key, valueLoader, var7);
}
}
} public RedisCacheElement get(RedisCacheKey cacheKey) {
Assert.notNull(cacheKey, "CacheKey must not be null!");
byte[] bytes = (byte[]) ((byte[]) this.redisOperations.execute(new CustomizeRedisCache.AbstractRedisCacheCallback<byte[]>(new CustomizeRedisCache.BinaryRedisCacheElement(new RedisCacheElement(cacheKey, (Object) null), this.cacheValueAccessor), this.cacheMetadata) {
public byte[] doInRedis(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
return connection.get(element.getKeyBytes());
}
}));
return bytes == null ? null : new RedisCacheElement(cacheKey, this.cacheValueAccessor.deserializeIfNecessary(bytes));
} public void put(Object key, Object value) {
this.put((new RedisCacheElement((new RedisCacheKey(key)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()), value)).expireAfter(this.cacheMetadata.getDefaultExpiration()));
} public void put(RedisCacheElement element) {
Assert.notNull(element, "Element must not be null!");
this.redisOperations.execute(new CustomizeRedisCache.RedisCachePutCallback(new CustomizeRedisCache.BinaryRedisCacheElement(element, this.cacheValueAccessor), this.cacheMetadata));
} public ValueWrapper putIfAbsent(Object key, Object value) {
return this.putIfAbsent((new RedisCacheElement((new RedisCacheKey(key)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()), value)).expireAfter(this.cacheMetadata.getDefaultExpiration()));
} public ValueWrapper putIfAbsent(RedisCacheElement element) {
Assert.notNull(element, "Element must not be null!");
new CustomizeRedisCache.RedisCachePutIfAbsentCallback(new CustomizeRedisCache.BinaryRedisCacheElement(element, this.cacheValueAccessor), this.cacheMetadata);
return this.toWrapper(this.cacheValueAccessor.deserializeIfNecessary((byte[]) ((byte[]) this.redisOperations.execute(new CustomizeRedisCache.RedisCachePutIfAbsentCallback(new CustomizeRedisCache.BinaryRedisCacheElement(element, this.cacheValueAccessor), this.cacheMetadata)))));
} /**
* 重点处理,进行重写
*
* @param key
*/
public void evict(Object key) {
if(key instanceof String){
String keyString=key.toString();
if(StringUtils.endsWith(keyString,"*")){
// evictLikePrefix(this.cacheMetadata.cacheName + keyString);
evictLikePrefix(keyString);
return;
}
if(StringUtils.startsWith(keyString,"*")){
// evictLikePrefix(this.cacheMetadata.cacheName + keyString);
evictLikePrefix(keyString);
return;
}
} // 原始
RedisCacheElement redisCacheElement = new RedisCacheElement((new RedisCacheKey(key)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()), (Object) null);
this.evict(redisCacheElement); } public void evict(RedisCacheElement element) {
Assert.notNull(element, "Element must not be null!");
this.redisOperations.execute(new CustomizeRedisCache.RedisCacheEvictCallback(new CustomizeRedisCache.BinaryRedisCacheElement(element, this.cacheValueAccessor), this.cacheMetadata));
} /**
* 进行模糊处理 key
*
* @param key
*/
public void evictLikePrefix(Object key){
Set keys = this.redisOperations.keys(key);
if(keys != null && keys.size() > 0){
for(Object k : keys){
RedisCacheElement redisCacheElement = new RedisCacheElement((new RedisCacheKey(k)).usePrefix(this.cacheMetadata.getKeyPrefix()).withKeySerializer(this.redisOperations.getKeySerializer()), (Object) null);
this.evict(redisCacheElement);
}
}
} public void clear() {
this.redisOperations.execute((RedisCallback) (this.cacheMetadata.usesKeyPrefix() ? new CustomizeRedisCache.RedisCacheCleanByPrefixCallback(this.cacheMetadata) : new CustomizeRedisCache.RedisCacheCleanByKeysCallback(this.cacheMetadata)));
} public String getName() {
return this.cacheMetadata.getCacheName();
} public Object getNativeCache() {
return this.redisOperations;
} private ValueWrapper toWrapper(Object value) {
return value != null ? new SimpleValueWrapper(value) : null;
} static class RedisCacheMetadata {
private final String cacheName;
private final byte[] keyPrefix;
private final byte[] setOfKnownKeys;
private final byte[] cacheLockName;
private long defaultExpiration = 0L; public RedisCacheMetadata(String cacheName, byte[] keyPrefix) {
Assert.hasText(cacheName, "CacheName must not be null or empty!");
this.cacheName = cacheName;
this.keyPrefix = keyPrefix;
StringRedisSerializer stringSerializer = new StringRedisSerializer();
this.setOfKnownKeys = this.usesKeyPrefix() ? new byte[0] : stringSerializer.serialize(cacheName + "~keys");
this.cacheLockName = stringSerializer.serialize(cacheName + "~lock");
} public boolean usesKeyPrefix() {
return this.keyPrefix != null && this.keyPrefix.length > 0;
} public byte[] getKeyPrefix() {
return this.keyPrefix;
} public byte[] getSetOfKnownKeysKey() {
return this.setOfKnownKeys;
} public byte[] getCacheLockKey() {
return this.cacheLockName;
} public String getCacheName() {
return this.cacheName;
} public void setDefaultExpiration(long seconds) {
this.defaultExpiration = seconds;
} public long getDefaultExpiration() {
return this.defaultExpiration;
}
} static class CacheValueAccessor {
private final RedisSerializer valueSerializer; CacheValueAccessor(RedisSerializer valueRedisSerializer) {
this.valueSerializer = valueRedisSerializer;
} byte[] convertToBytesIfNecessary(Object value) {
if (value == null) {
return new byte[0];
} else {
return this.valueSerializer == null && value instanceof byte[] ? (byte[]) ((byte[]) value) : this.valueSerializer.serialize(value);
}
} Object deserializeIfNecessary(byte[] value) {
return this.valueSerializer != null ? this.valueSerializer.deserialize(value) : value;
}
} static class RedisCachePutIfAbsentCallback extends CustomizeRedisCache.AbstractRedisCacheCallback<byte[]> {
public RedisCachePutIfAbsentCallback(CustomizeRedisCache.BinaryRedisCacheElement element, CustomizeRedisCache.RedisCacheMetadata metadata) {
super(element, metadata);
} public byte[] doInRedis(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
this.waitForLock(connection);
byte[] resultValue = this.put(element, connection);
if (ObjectUtils.nullSafeEquals(element.get(), resultValue)) {
this.processKeyExpiration(element, connection);
this.maintainKnownKeys(element, connection);
} return resultValue;
} private byte[] put(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) {
boolean valueWasSet = connection.setNX(element.getKeyBytes(), element.get()).booleanValue();
return valueWasSet ? null : connection.get(element.getKeyBytes());
}
} static class RedisCacheEvictCallback extends CustomizeRedisCache.AbstractRedisCacheCallback<Void> {
public RedisCacheEvictCallback(CustomizeRedisCache.BinaryRedisCacheElement element, CustomizeRedisCache.RedisCacheMetadata metadata) {
super(element, metadata);
} public Void doInRedis(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
connection.del(new byte[][]{element.getKeyBytes()});
this.cleanKnownKeys(element, connection);
return null;
}
} static class RedisCachePutCallback extends CustomizeRedisCache.AbstractRedisCacheCallback<Void> {
public RedisCachePutCallback(CustomizeRedisCache.BinaryRedisCacheElement element, CustomizeRedisCache.RedisCacheMetadata metadata) {
super(element, metadata);
} public Void doInRedis(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
connection.multi();
connection.set(element.getKeyBytes(), element.get());
this.processKeyExpiration(element, connection);
this.maintainKnownKeys(element, connection);
connection.exec();
return null;
}
} private static enum CacheValueRetrievalExceptionFactory {
INSTANCE; private static boolean isSpring43 = ClassUtils.isPresent("org.springframework.cache.Cache$ValueRetrievalException", ClassUtils.getDefaultClassLoader()); private CacheValueRetrievalExceptionFactory() {
} public RuntimeException create(Object key, Callable<?> valueLoader, Throwable cause) {
if (isSpring43) {
try {
Class<?> execption = ClassUtils.forName("org.springframework.cache.Cache$ValueRetrievalException", this.getClass().getClassLoader());
Constructor<?> c = ClassUtils.getConstructorIfAvailable(execption, new Class[]{Object.class, Callable.class, Throwable.class});
return (RuntimeException) c.newInstance(key, valueLoader, cause);
} catch (Exception var6) {
;
}
} return new RedisSystemException(String.format("Value for key '%s' could not be loaded using '%s'.", key, valueLoader), cause);
}
} static class RedisCacheCleanByPrefixCallback extends CustomizeRedisCache.LockingRedisCacheCallback<Void> {
private static final byte[] REMOVE_KEYS_BY_PATTERN_LUA = (new StringRedisSerializer()).serialize("local keys = redis.call('KEYS', ARGV[1]); local keysCount = table.getn(keys); if(keysCount > 0) then for _, key in ipairs(keys) do redis.call('del', key); end; end; return keysCount;");
private static final byte[] WILD_CARD = (new StringRedisSerializer()).serialize("*");
private final CustomizeRedisCache.RedisCacheMetadata metadata; public RedisCacheCleanByPrefixCallback(CustomizeRedisCache.RedisCacheMetadata metadata) {
super(metadata);
this.metadata = metadata;
} public Void doInLock(RedisConnection connection) throws DataAccessException {
byte[] prefixToUse = Arrays.copyOf(this.metadata.getKeyPrefix(), this.metadata.getKeyPrefix().length + WILD_CARD.length);
System.arraycopy(WILD_CARD, 0, prefixToUse, this.metadata.getKeyPrefix().length, WILD_CARD.length);
connection.eval(REMOVE_KEYS_BY_PATTERN_LUA, ReturnType.INTEGER, 0, new byte[][]{prefixToUse});
return null;
}
} abstract static class LockingRedisCacheCallback<T> implements RedisCallback<T> {
private final CustomizeRedisCache.RedisCacheMetadata metadata; public LockingRedisCacheCallback(CustomizeRedisCache.RedisCacheMetadata metadata) {
this.metadata = metadata;
} public T doInRedis(RedisConnection connection) throws DataAccessException {
if (connection.exists(this.metadata.getCacheLockKey()).booleanValue()) {
return null;
} else {
Object var2;
try {
connection.set(this.metadata.getCacheLockKey(), this.metadata.getCacheLockKey());
var2 = this.doInLock(connection);
} finally {
connection.del(new byte[][]{this.metadata.getCacheLockKey()});
} return (T) var2;
}
} public abstract T doInLock(RedisConnection var1);
} static class RedisCacheCleanByKeysCallback extends CustomizeRedisCache.LockingRedisCacheCallback<Void> {
private static final int PAGE_SIZE = 128;
private final CustomizeRedisCache.RedisCacheMetadata metadata; RedisCacheCleanByKeysCallback(CustomizeRedisCache.RedisCacheMetadata metadata) {
super(metadata);
this.metadata = metadata;
} public Void doInLock(RedisConnection connection) {
int offset = 0;
boolean finished = false; do {
Set<byte[]> keys = connection.zRange(this.metadata.getSetOfKnownKeysKey(), (long) (offset * 128), (long) ((offset + 1) * 128 - 1));
finished = keys.size() < 128;
++offset;
if (!keys.isEmpty()) {
connection.del((byte[][]) keys.toArray(new byte[keys.size()][]));
}
} while (!finished); connection.del(new byte[][]{this.metadata.getSetOfKnownKeysKey()});
return null;
}
} static class BinaryRedisCacheElement extends RedisCacheElement {
private byte[] keyBytes;
private byte[] valueBytes;
private RedisCacheElement element;
private boolean lazyLoad;
private CustomizeRedisCache.CacheValueAccessor accessor; public BinaryRedisCacheElement(RedisCacheElement element, CustomizeRedisCache.CacheValueAccessor accessor) {
super(element.getKey(), element.get());
this.element = element;
this.keyBytes = element.getKeyBytes();
this.accessor = accessor;
this.lazyLoad = element.get() instanceof Callable;
this.valueBytes = this.lazyLoad ? null : accessor.convertToBytesIfNecessary(element.get());
} public byte[] getKeyBytes() {
return this.keyBytes;
} public long getTimeToLive() {
return this.element.getTimeToLive();
} public boolean hasKeyPrefix() {
return this.element.hasKeyPrefix();
} public boolean isEternal() {
return this.element.isEternal();
} public RedisCacheElement expireAfter(long seconds) {
return this.element.expireAfter(seconds);
} public byte[] get() {
if (this.lazyLoad && this.valueBytes == null) {
try {
this.valueBytes = this.accessor.convertToBytesIfNecessary(((Callable) this.element.get()).call());
} catch (Exception var2) {
throw var2 instanceof RuntimeException ? (RuntimeException) var2 : new RuntimeException(var2.getMessage(), var2);
}
} return this.valueBytes;
}
} abstract static class AbstractRedisCacheCallback<T> implements RedisCallback<T> {
private long WAIT_FOR_LOCK_TIMEOUT = 300L;
private final CustomizeRedisCache.BinaryRedisCacheElement element;
private final CustomizeRedisCache.RedisCacheMetadata cacheMetadata; public AbstractRedisCacheCallback(CustomizeRedisCache.BinaryRedisCacheElement element, CustomizeRedisCache.RedisCacheMetadata metadata) {
this.element = element;
this.cacheMetadata = metadata;
} public T doInRedis(RedisConnection connection) throws DataAccessException {
this.waitForLock(connection);
return this.doInRedis(this.element, connection);
} public abstract T doInRedis(CustomizeRedisCache.BinaryRedisCacheElement var1, RedisConnection var2) throws DataAccessException; protected void processKeyExpiration(RedisCacheElement element, RedisConnection connection) {
if (!element.isEternal()) {
connection.expire(element.getKeyBytes(), element.getTimeToLive());
} } protected void maintainKnownKeys(RedisCacheElement element, RedisConnection connection) {
if (!element.hasKeyPrefix()) {
connection.zAdd(this.cacheMetadata.getSetOfKnownKeysKey(), 0.0D, element.getKeyBytes());
if (!element.isEternal()) {
connection.expire(this.cacheMetadata.getSetOfKnownKeysKey(), element.getTimeToLive());
}
} } protected void cleanKnownKeys(RedisCacheElement element, RedisConnection connection) {
if (!element.hasKeyPrefix()) {
connection.zRem(this.cacheMetadata.getSetOfKnownKeysKey(), new byte[][]{element.getKeyBytes()});
} } protected boolean waitForLock(RedisConnection connection) {
boolean foundLock = false; boolean retry;
do {
retry = false;
if (connection.exists(this.cacheMetadata.getCacheLockKey()).booleanValue()) {
foundLock = true; try {
Thread.sleep(this.WAIT_FOR_LOCK_TIMEOUT);
} catch (InterruptedException var5) {
Thread.currentThread().interrupt();
} retry = true;
}
} while (retry); return foundLock;
} protected void lock(RedisConnection connection) {
this.waitForLock(connection);
connection.set(this.cacheMetadata.getCacheLockKey(), "locked".getBytes());
} protected void unlock(RedisConnection connection) {
connection.del(new byte[][]{this.cacheMetadata.getCacheLockKey()});
}
} static class RedisWriteThroughCallback extends CustomizeRedisCache.AbstractRedisCacheCallback<byte[]> {
public RedisWriteThroughCallback(CustomizeRedisCache.BinaryRedisCacheElement element, CustomizeRedisCache.RedisCacheMetadata metadata) {
super(element, metadata);
} public byte[] doInRedis(CustomizeRedisCache.BinaryRedisCacheElement element, RedisConnection connection) throws DataAccessException {
byte[] var4;
try {
this.lock(connection); try {
byte[] value = connection.get(element.getKeyBytes());
if (value != null) {
var4 = value;
return var4;
} connection.watch(new byte[][]{element.getKeyBytes()});
connection.multi();
value = element.get();
connection.set(element.getKeyBytes(), value);
this.processKeyExpiration(element, connection);
this.maintainKnownKeys(element, connection);
connection.exec();
var4 = value;
} catch (RuntimeException var8) {
connection.discard();
throw var8;
}
} finally {
this.unlock(connection);
} return var4;
}
} }

可以关注来获取对应的源码

springmvc redis @Cacheable扩展(一)的更多相关文章

  1. redis php扩展安装下载

    php的redis 扩展下载地址 Windows :http://windows.php.net/downloads/pecl/releases/redis/2.2.7/ 下载对应版本,一般有两个 n ...

  2. php7对redis的扩展及redis主从搭建

    这两天在学习一下php7下面的安装及redis相关配置认识.并将笔记记下来.以备后用.主要涉及到redis的扩展php-redis 及redis主从的配置. 一:redis安装     1:下载并安装 ...

  3. SpringMVC+redis整合

    在网络上有一个很多人转载的springmvc+redis整合的案例,不过一直不完整,也是被各种人装来转去,现在基本将该框架搭建起来. package com.pudp.bae.base; import ...

  4. redis和redis php扩展安装(转)

    redis是一个内存数据库,比memcache支持更丰富的value类型,新浪微博就使用redis来做缓存. redis的源码安装 wget http://download.redis.io/redi ...

  5. nginx/php的redis模块扩展

    redis模块介绍 redis2-nginx-module 可以实现 Nginx 以非阻塞方式直接防问远方的 Redis 服务,可以启用强大的 Redis 连接池功能,进而实现更多的连接与更快速的访问 ...

  6. spring redis @Cacheable注解使用部分错误及无效原因

    spring redis @Cacheable注解使用部分错误及无效原因 说明:     spring项目用到redis注解无效,解决问题中遇到一堆BUG,各种搜索,看了许多错误解决方案一一测试,对于 ...

  7. redis php扩展及基本命令

    linux 安装php mysql redis memchache 等工具 用 OneinStack 安装步骤 注意 如果有单独数据盘,建议您先挂载数据盘,建议将网站内容.数据库放在数据盘中.如何挂载 ...

  8. redis水平扩展实践,完全配置,无需代码改动

    设计思路 思路很简单,就是基于用户ID进行分库,将用户的ID字符串按照byte逐个计算ID对应的hash原值(一个数字,取绝对值,因为原始值可能过大溢出,变成负数),然后,再用这个hash原值对库的个 ...

  9. inux redis 安装配置, 以及redis php扩展

    一,什么是redis redis是一个key-value存储系统. 和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合)和zset ...

随机推荐

  1. PowerShell随笔1---背景

    既然是随笔,那就想到什么说什么,既会分享主题知识,也会分享一些其他技巧和个人学习方法,供交流. 我一般学习一个东西,我都会问几个问题: 这东西是什么? 这东西有什么用,为什么会出现,出现是为了解决什么 ...

  2. Python小练习批量爬取下载歌曲

    import requests import os headers={ 'Cookie': '_ga=GA1.2.701818100.1612092981; _gid=GA1.2.748589379. ...

  3. LEETCODE - 1181【前后拼接】

    class Solution { public: string gethead(string str){//获取头单词 string ret = ""; int strlen = ...

  4. codefroces 7C

    C. Line time limit per test 1 second memory limit per test 256 megabytes input standard input output ...

  5. codeforces 5C

    C. Longest Regular Bracket Sequence time limit per test 2 seconds memory limit per test 256 megabyte ...

  6. HDU 3681 Prison Break(状压DP + BFS)题解

    题意:一张图,F是起点,Y是必须要到的点,D不能走,G可以充电.可以往四个方向走,每走一步花费一个电,走到G可以选择充满电或者不充,每个G只能充一次.问你走遍Y的最小初始点亮.number(G) + ...

  7. Promise thenable All In One

    Promise thenable All In One Promise thenable 是指一个函数或一个对象的里面定义了一个 then 方法 Promises/A+ https://promise ...

  8. chroot vs docker

    chroot vs docker chroot Linux A chroot on Unix operating systems is an operation that changes the ap ...

  9. free ebooks all in one

    free ebooks all in one pdf / ppt mobi / epub free programming ebooks free IT ebooks open free ebooks ...

  10. flutter & dart & vs code & bug

    flutter & dart & vs code & bug Waiting for another flutter command to release the startu ...