1、背景
咱们在项目当中常常要使用redis做为缓存,可选择的方式有:1)编程方式 2)注解方式java
1. 编程方式
jedis
redisTempate
redisson
…
2. 注解方式
spring cache系列注解
经过切面自定义注解实现
因为spring @Cacheable提供了多种缓存实现,有的缓存是不支持ttl的,所以@Cacheable注解当中是没有设置ttl参数的,Redis实现的Cache,配置ttl必须单独集中配置,以下:
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaultConfig);
Map cacheConfigurations = new HashMap<>();
// 自定义每一个cacheName的配置
RedisCacheConfiguration config = cacheDefault(resourceLoader);
config = config.entryTtl(Duration.ofMinutes(30));
cacheConfigurations.put("store", config);
cacheConfigurations.put("store-nearby", defaultConfig);
return builder.withInitialCacheConfigurations(cacheConfigurations).build();
这样作有spring的道理,可是确实感受不是很灵活,团队以前也基于切面实现了一套支持ttl的注解。最近项目架构升级到spring-boot,能不能基于spring-boot-data-redis扩展实现注解方式的ttl?git
2、思路
1. 增长切面
好比,咱们自定义一个@Ttl注解,若是一个方法上面同时有@Cacheable @Ttl注解,咱们解析对应Redis Key, 方法执行结束后,给对应的key设置过时时间web
2. 找SpringCache扩展点
Debug代码,寻找扩展点进行扩展。redis
通过比较,我发现spring生成key的策略是比较复杂的,RedisCacheConfiguration能够配置前缀,KeyGenerator又能够自定义配置key的生成策略。实现代码解析key难度较大,而且容易出现bug,直接放弃这种方式。由于咱们要经过反射拿到方法上的ttl时间,咱们这个扩展点首先要能获取当前执行的Method,奔着目标,debug代码,我把目标放在了RedisCache、CacheAspectSupport、CacheResolver、RedisCacheManager这几个类上。spring
RedisCache
@Override
public void put(Object key, @Nullable Object value) {
Object cacheValue = preProcessCacheValue(value);
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));
}
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
重点代码编程
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
这一行代码,我能够看出来,ttl是从全局变量cacheConfig的getTtl()获取的,下一步我要找到RedisCache的实例是何时生成并注入cacheConfig的。缓存
CacheAspectS