今天在研究缓存,@Cacheable注解,可设置value,key,cacheManage,key可以是相关的传入值作为参数,下面有栗子,想设置单个key的缓存时间,但是没有,只是全局设置,要么不设置,这样很大可能会造成缓存一时全失效,有个专业词我也不记得了。
查看源码得知,有一个类对redis进行处理,是DefaultRedisCacheWriter,但是此类是没有被修饰符所修饰的,所以只能包类使用,不能被继承,所以也不能被重写方法
里面有几个方法:
class DefaultRedisCacheWriter implements RedisCacheWriter {
@Override
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
。。。
}
@Override
public byte[] get(String name, byte[] key) {
。。。
}
}
所以只能实现RedisCacheWriter ,里面有4个方法,实现即可,我是基本上从DefaultRedisCacheWriter上拷贝实现,然后加以修改的,莫见怪
void put(String name, byte[] key, byte[] value, @Nullable Duration ttl);
/**
* Get the binary value representation from Redis stored for the given key.
*
* @param name must not be {@literal null}.
* @param key must not be {@literal null}.
* @return {@literal null} if key does not exist.
*/
@Nullable
byte[] get(String name, byte[] key);
/**
* Write the given value to Redis if the key does not already exist.
*
* @param name The cache name must not be {@literal null}.
* @param key The key for the cache entry. Must not be {@literal null}.
* @param value The value stored for the key. Must not be {@literal null}.
* @param ttl Optional expiration time. Can be {@literal null}.
* @return {@literal null} if the value has been written, the value stored for the key if it already exists.
*/
@Nullable
byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl);
/**
* Remove the given key from Redis.
*
* @param name The cache name must not be {@literal null}.
* @param key The key for the cache entry. Must not be {@literal null}.
*/
void remove(String name, byte[] key);
/**
* Remove all keys following the given pattern.
*
* @param name The cache name must not be {@literal null}.
* @param pattern The pattern for the keys to remove. Must not be {@literal null}.
*/
void clean(String name, byte[] pattern);
下面就是实现了,且看
第一步,实现(这里只把put方法拷贝进来,其他的都可自行实现)
//rediskey相关类
public class RedisKeys {
final static String REDIS_EXPIRE_TIME_KEY = "#key_expire_time";
}
//缓存写入实现
public class RedisCacheWriterCustomer implements RedisCacheWriter {
private final RedisConnectionFactory connectionFactory;
private final Duration sleepTime;
/**
* @param connectionFactory must not be {@literal null}.
*/
RedisCacheWriterCustomer(RedisConnectionFactory connectionFactory) {
this(connectionFactory, Duration.ZERO);
}
/**
* @param connectionFactory must not be {@literal null}.
* @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO}
* to disable locking.
*/
RedisCacheWriterCustomer(RedisConnectionFactory connectionFactory, Duration sleepTime) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
Assert.notNull(sleepTime, "SleepTime must not be null!");
this.connectionFactory = connectionFactory;
this.sleepTime = sleepTime;
}
@Override
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
execute(name, connection -> {
//当设置了过期时间,则修改取出
//@Cacheable(value="user-key#key_expire=1200",key = "#id",condition = "#id != 2")
//name 对应 value
//key 对应 value :: key
//判断name里面是否设置了过期时间,如果设置了则对key进行缓存,并设置过期时间
int index = name.lastIndexOf(RedisKeys.REDIS_EXPIRE_TIME_KEY);
if (index > 0){
//取出对应的时间 1200 index + 1是还有一个=号
String expireString = name.substring(index + 1 + RedisKeys.REDIS_EXPIRE_TIME_KEY.length());
long expireTime = Long.parseLong(expireString);
connection.set(key, value, Expiration.from(expireTime,TimeUnit.SECONDS), RedisStringCommands.SetOption.upsert());
}else if (shouldExpireWithin(ttl)) {
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.upsert());
} else {
connection.set(key, value);
}
return "OK";
});
}
}
第二步: 把实现的类加入到缓存管理器中
//新建一个配置类
@Configuration
@EnableCaching //开启缓存,默认是rendis缓存,继承CachingConfigurerSupport ,直接重写里面的方法
public class RedisConfig extends CachingConfigurerSupport {
@Autowired
private RedisTemplate redisTemplate;
@Override
@Bean
public CacheManager cacheManager() {
RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
//上面实现的缓存读写
RedisCacheWriterCustomer cachaWriterCustomer
= new RedisCacheWriterCustomer(connectionFactory);
CacheManager cm
= new RedisCacheManager(cachaWriterCustomer,redisCacheConfiguration());
return cm;
}
@Bean
public RedisCacheConfiguration redisCacheConfiguration(){
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())).entryTtl(Duration.ofSeconds(30));
return configuration;
}
}
第三步 :在需要的方法上添加注解即可
@RequestMapping("/cacheable")
//只是此处有点不雅观
@Cacheable(value="user-key"+ RedisKeys.REDIS_EXPIRE_TIME_KEY +"="+ 1200,key = "#id",condition = "#id != 2")
@ResponseBody
public Product CacheableTest(Long id){
System.out.println("你可还好");
return "你可还好";
}
所以现在每次都是从缓存中取,并且设置了过期时间,这里并没有实现重新拿更新过期时间,有需要的再到get()方法中实现。
完。。。