Spring cache的源碼版本:spring-context-5.0.9.RELEASE.jar
項(xiàng)目demo代碼:點(diǎn)我跳轉(zhuǎn)
先講自定義可以干嘛任斋,再講解源碼:
通過(guò)自定義cache config,可以用來(lái)設(shè)置自定義的過(guò)期時(shí)間崔泵,自定義的序列化方式挂滓,自定義前綴等等嫉髓。@Cacheable
注解不能設(shè)置過(guò)期時(shí)間倘零,這點(diǎn)是由于cache本身是抽象,各種實(shí)現(xiàn)過(guò)期時(shí)間的一些具體緩存框架可能有差異幻梯,不過(guò)我覺(jué)得這是一個(gè)非常不爽的點(diǎn)兜畸。
所以我們來(lái)閱讀源代碼吧。
Cache啟動(dòng)初始化
AbstractCacheManager
類中有一個(gè)cacheMap
變量存儲(chǔ)所有的緩存實(shí)現(xiàn)碘梢,在項(xiàng)目初始化時(shí)咬摇,由于類中實(shí)現(xiàn)了InitializingBean
接口,所有會(huì)初始化緩存煞躬,代碼:
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
@Override
public void afterPropertiesSet() {
initializeCaches();
}
/**
* Initialize the static configuration of caches.
* <p>Triggered on startup through {@link #afterPropertiesSet()};
* can also be called to re-initialize at runtime.
* @since 4.2.2
* @see #loadCaches()
*/
public void initializeCaches() {
// 1??重點(diǎn)在loadCaches方法
Collection<? extends Cache> caches = loadCaches();
synchronized (this.cacheMap) {
this.cacheNames = Collections.emptySet();
this.cacheMap.clear();
Set<String> cacheNames = new LinkedHashSet<>(caches.size());
for (Cache cache : caches) {
String name = cache.getName();
this.cacheMap.put(name, decorateCache(cache));
cacheNames.add(name);
}
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
}
}
由于loadCaches方法是抽象的肛鹏,我們實(shí)現(xiàn)使用的redis實(shí)現(xiàn),所有直接查看org.springframework.data.redis.cache.RedisCacheManager
類的實(shí)現(xiàn)恩沛,閱讀源代碼發(fā)現(xiàn):
public class RedisCacheManager extends AbstractTransactionSupportingCacheManager {
...
private final Map<String, RedisCacheConfiguration> initialCacheConfiguration;
...
@Override
protected Collection<RedisCache> loadCaches() {
//1??可以看到實(shí)際上就是取initialCacheConfiguration變量的值
List<RedisCache> caches = new LinkedList<>();
for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
//2??初始化cache
caches.add(createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
...
}
通過(guò)注入自定義的cacheConfig能夠使不同的key擁有不同的cache配置在扰,達(dá)到自定義的效果。
Cache被調(diào)用
回到上面的正題雷客,在cacheManager
初始化完成后芒珠,當(dāng)有請(qǐng)求來(lái)到@Cacheable注解處的方法時(shí),會(huì)通過(guò)aop代理的形式做invoke佛纫,頂層是在CacheAspectSupport
的execute方法進(jìn)行代理妓局,
中間一個(gè)步驟省略,它最后會(huì)直接通過(guò)CacheManager去獲取cache呈宇,方法為:
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
...
@Override
@Nullable
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 = getMissingCache(name);
if (cache != null) {
cache = decorateCache(cache);
this.cacheMap.put(name, cache);
updateCacheNames(name);
}
}
return cache;
}
}
}
...
}
我們查看下RedisCache
內(nèi)部調(diào)用生成緩存的方法來(lái)看一下。
public class RedisCache extends AbstractValueAdaptingCache {
@Override
public void put(Object key, @Nullable Object value) {
Object cacheValue = preProcessCacheValue(value);
...
//1?? 過(guò)期時(shí)間是通過(guò)cacheConfig配置進(jìn)行獲取的局雄。
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
protected byte[] serializeCacheValue(Object value) {
if (isAllowNullValues() && value instanceof NullValue) {
return BINARY_NULL_VALUE;
}
//2?? value的序列化方式也是通過(guò)cacheConfig配置來(lái)初始化的
return ByteUtils.getBytes(cacheConfig.getValueSerializationPair().write(value));
}
}
自定義CacheConfig的配置方法
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(connectionFactory);
builder.withInitialCacheConfigurations(customCacheConfig());
return builder.build();
}
private Map<String, RedisCacheConfiguration> customCacheConfig() {
Map<String, RedisCacheConfiguration> map = new HashMap<>();
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(1)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
PS: 感覺(jué)使用Spring cache還是略麻煩甥啄,不如自己實(shí)現(xiàn)一個(gè)基于aop的cache吧。