准备工作
- 使用spring boot搭建简单的项目
引入redis的包
compile('org.springframework.boot:spring-boot-starter-data-redis')
配置application.yml文件,添加如下内容(redis其他可以先使用默认配置)
spring: cache: type: redis redis: database: 0
源码分析
- 在需要缓存的方法上面加上@Cacheable
当请求进来的时候会触发CacheInterceptor拦截器,调用invoke方法
@Override public Object invoke(final MethodInvocation invocation) throws Throwable { Method method = invocation.getMethod(); CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() { @Override public Object invoke() { try { return invocation.proceed(); } catch (Throwable ex) { throw new ThrowableWrapper(ex); } } }; try { return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments()); } catch (CacheOperationInvoker.ThrowableWrapper th) { throw th.getOriginal(); } }
invoke方法会去调用CacheAspectSupport的execute方法
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically) if (this.initialized) { Class<?> targetClass = getTargetClass(target); Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass); if (!CollectionUtils.isEmpty(operations)) { return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass)); } } return invoker.invoke(); }
然后会调用到cachemanager的getCache方法
@Override public Cache getCache(String name) { Cache cache = this.cacheMap.get(name); if (cache != null) { return cache; } else { // Fully synchronize now for missing cache creation... synchronized (this.cacheMap) { cache = this.cacheMap.get(name); if (cache == null) { //cache不存在时,会调用该方法取cache,该方法由子类实现 cache = getMissingCache(name); if (cache != null) { cache = decorateCache(cache); this.cacheMap.put(name, cache); updateCacheNames(name); } } return cache; } } }
再看RedisCacheManager类
@Override protected Cache getMissingCache(String name) { return this.dynamic ? createCache(name) : null; } @SuppressWarnings("unchecked") protected RedisCache createCache(String cacheName) { long expiration = computeExpiration(cacheName); return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration, cacheNullValues); } // 根据cacheName取过期时间 protected long computeExpiration(String name) { Long expiration = null; if (expires != null) { expiration = expires.get(name); } return (expiration != null ? expiration.longValue() : defaultExpiration); }
代码实现
- 分析源码后,我们可以在createCache方法上做文章,实现自己的cacheManager
代码如下:
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;
/**
* @author zhangminglei
*/
public class CustomizeRedis extends RedisCacheManager {
CustomizeRedis(RedisOperations redisOperations) {
super(redisOperations);
}
@Override
protected RedisCache createCache(String cacheName) {
Assert.hasText(cacheName, "CacheName must not be null or empty!");
String[] values = cacheName.split("#");
long expiration = computeExpiration(values);
return new RedisCache(values[0], (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(), expiration,
false);
}
private long computeExpiration(String[] values) {
if (values.length > 1) {
return Long.parseLong(values[1]);
}
// 如果说想使用默认的过期时间而不指定特殊时间,则可以直接@Cacheable(cacheNames="name"),不需要加'#过期时间'了
return super.computeExpiration(values[0]);
}
}
- 然后需要让cache使用我们的自定义CacheManager
// 为redis设定默认的序列化方式为StringRedisSerializer(默认为JdkSerializationRedisSerializer,不太方便查看内容)
@Bean
RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setDefaultSerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean
public RedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {
RedisCacheManager cacheManager = new CustomizeRedis(redisTemplate);
cacheManager.setUsePrefix(false);
//设置默认的过期时间,单位秒
cacheManager.setDefaultExpiration(60);
// 还可以使用下面的方法为指定的key设定过期时间,它将会在computeExpiration方法中用到
// Map<String, Long> expires = new HashMap<>();
// expires.put("cacheNameKey", 20L);
// expires.put("myKey", 40L);
// cacheManager.setExpires(expires);
return cacheManager;
}
测试代码
//指定cacheNameKey的缓存45秒后过期
@Cacheable(cacheNames = "cacheNameKey#45", key = "#key")
public String testRedisCache(String key) {
System.out.println(key);
return "12345";
}