關(guān)于Spring Cache的介紹,參考:http://www.reibang.com/p/51389fbaf411,本文主要講關(guān)于注解@Cacheable
, @CachePut
, @CacheEvict
背后是怎樣實(shí)現(xiàn)的瓤荔。
1. 相關(guān)類介紹
-
1.1
Cache
,CacheManager
: 這兩個(gè)接口是Spring Cache的核心接口洞就。-
Cache
接口主要定義了cache的操作,如get, put等轴脐。 -
CacheManager
接口定義的是cache的集合,為什么會有多個(gè)Cache對象主要是每個(gè)cache可能會有自己的過期時(shí)間、Cache的元素需要被限制等原因匀哄。
-
-
1.2
CacheInterceptor
,CacheAspectSupport
,AbstractCacheInvoker
:標(biāo)記Cache注解的方法被調(diào)用時(shí)的AOP相關(guān)的類。-
CacheInterceptor
繼承了AOP的MethodInterceptor
雏蛮,其中方法invoke(invocation)
方法相當(dāng)于@Aspect
中的Around
涎嚼,可以在目標(biāo)方法之前和之后寫一些額外的邏輯,比如從cache中查詢數(shù)據(jù)挑秉,或是寫數(shù)據(jù)至cache等法梯。 -
CacheAspectSupport
:真正提供cache操作的類,被上述的CacheInterceptor
繼承犀概。 -
AbstractCacheInvoker
:抽象類立哑,被上述的CacheAspectSupport
繼續(xù),提供讀cache姻灶、寫cache等操作铛绰,如方法:doGet(cache, key) , doPut(cache, key, result)等。
-
-
1.3
CacheOperation
,AnnotationCacheOperationSource
,SpringCacheAnnotationParser
:主要是為annotation做解析的木蹬。-
CacheOperation
是個(gè)抽象類至耻,定義了cache的name\key\condition等,繼承它的主要也是上述三個(gè)注解的operation镊叁,分別是:CacheableOperation
,CachePutOperation
,CacheEvictOperation
尘颓。 -
AnnotationCacheOperationSource
類主要是讀取Spring Cache的三個(gè)annotation(@Cacheable
,@CachePut
,@CacheEvict
),轉(zhuǎn)成CacheOperation
晦譬。 -
SpringCacheAnnotationParser
類則負(fù)責(zé)具體的annotation parse邏輯疤苹。
-
2. Annotation解析
2.1 SpringCacheAnnotationParser
類
在1.3中介紹,SpringCacheAnnotationParser
類負(fù)責(zé)具體的annotation parse邏輯敛腌。
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
if (ops != null && ops.size() > 1) {
// More than one operation found -> local declarations override interface-declared ones...
Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
if (localOps != null) {
return localOps;
}
}
return ops;
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
Collection<? extends Annotation> anns = (localOnly ?
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
final Collection<CacheOperation> ops = new ArrayList<>(1);
anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
anns.stream().filter(ann -> ann instanceof CachePut).forEach(
ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
// 具體的parse邏輯:
private CacheableOperation parseCacheableAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
CacheableOperation.Builder builder = new CacheableOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheable.cacheNames());
builder.setCondition(cacheable.condition());
builder.setUnless(cacheable.unless());
builder.setKey(cacheable.key());
builder.setKeyGenerator(cacheable.keyGenerator());
builder.setCacheManager(cacheable.cacheManager());
builder.setCacheResolver(cacheable.cacheResolver());
builder.setSync(cacheable.sync());
defaultConfig.applyDefault(builder);
CacheableOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
}
比如我CourseService
有個(gè)方法getById(int)上有@Cacheable
注解:
@Cacheable(cacheNames = "courses")
public Course getById(int id) {...}
Debug上述parseCacheAnnotations()
方法的結(jié)果卧土,可以看到CourseService
上的注解,最終會被轉(zhuǎn)成CacheOperation
對象像樊,這個(gè)對象在#1.3有介紹過尤莺,主要是存放了注解的name, key, condition等信息:
【小結(jié)】SpringCacheAnnotationParser
類圖:
2.2 AnnotationCacheOperationSource
類
那么是誰在調(diào)用上述#2.1的parseCacheAnnotations()
方法?
AnnotationCacheOperationSource
在構(gòu)造方法中new了上述的SpringCacheAnnotationParser
生棍,并且在findCacheOperations(method)
方法中調(diào)用了#2.1的parse方法:
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
}
那么颤霎,findCacheOperations(method)
是誰在調(diào)用呢?
AnnotationCacheOperationSource
繼續(xù)了抽象類AbstractFallbackCacheOperationSource
,在抽象類中:
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
}
@Nullable
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Don't allow non-public methods, as configured.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}
// Second try is the caching operation on the target class.
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// Last fallback is the class of the original method.
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
return null;
}
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
}
抽象類中:方法getCacheOperations(method, targetClass)
調(diào)用了 --> computeCacheOperations(method, targetClass)
友酱,調(diào)用了抽象方法 --> findCacheOperations(method)
而方法getCacheOperations(method, targetClass)
則在第3章介紹的類中被調(diào)用晴音。
【小結(jié)】打星的方法即是annotation parse中最終會被外部調(diào)用的方法:3. 目標(biāo)方法被執(zhí)行時(shí)的邏輯
本章節(jié)圍繞CacheInterceptor
相關(guān)類展開。
@Cacheable
的定義是缔杉,在執(zhí)行目標(biāo)方法前锤躁,先查一遍緩存,如果緩存里有或详,那么就返回結(jié)果系羞,如果沒有,那么執(zhí)行目標(biāo)方法鸭叙,并將結(jié)果寫入緩存觉啊。
不難想象,用切面實(shí)現(xiàn)比較容易沈贝,即目標(biāo)方法前做一些邏輯(查緩存杠人,返回結(jié)果)宋下,目標(biāo)方法后做一些邏輯(將結(jié)果寫入緩存)嗡善。
創(chuàng)建CacheInterceptor
由類ProxyCachingConfiguration
負(fù)責(zé)創(chuàng)建CacheInterceptor
,該類位于spring-context包中,這個(gè)類本身是個(gè)Configuration枝笨,最終由@EnableCaching
負(fù)責(zé)激活横浑。
關(guān)于@EnableCache相關(guān)洒缀,具體參考:http://it.cha138.com/nginx/show-291168.html
類ProxyCachingConfiguration
具體源碼,具體的解釋在源碼下面:
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
CacheInterceptor interceptor = new CacheInterceptor();
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource);
return interceptor;
}
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
advisor.setCacheOperationSource(cacheOperationSource);
advisor.setAdvice(cacheInterceptor);
if (this.enableCaching != null) {
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
}
return advisor;
}
}
上述定義的ProxyCachingConfiguration
砰奕,主要做了3件事:
- a. 創(chuàng)建了我們上述第#2章的
CacheOperationSource
接口實(shí)現(xiàn)胸哥。 - b. 創(chuàng)建了
CacheInterceptor
银酬,并將a的值set到該類中。CacheInterceptor
實(shí)現(xiàn)了MethodInterceptor
接口李破,定義了切面方法的邏輯嗤攻。 - c. 定義Advisor承粤,并定義切點(diǎn)(PointCut)辛臊,將和上述b綁定在一起淹遵。這里定義的切點(diǎn)類為
CacheOperationSourcePointcut
,其中重要的方法(即每次匹配要用到的邏輯):matches()
辐真,這里用到了第2章的annotation parse方法:
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
@Override
public boolean matches(Method method, Class<?> targetClass) {
CacheOperationSource cas = getCacheOperationSource();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
}
【小結(jié)】左邊是第2章的類圖撩轰,右邊是本章的類圖:3.2 詳解CacheInterceptor
的invoke(invocation)
方法
CacheInterceptor
類源碼:
- 首先它實(shí)現(xiàn)了aop中的接口
MethodInterceptor
木柬。 - 其次眉枕,繼承了
CacheAspectSupport
類拓哟,該類提供的execute()方法即為具體的邏輯疮蹦。
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
return invocation.proceed();
}
catch (Throwable ex) {
throw new CacheOperationInvoker.ThrowableWrapper(ex);
}
};
Object target = invocation.getThis();
Assert.state(target != null, "Target must not be null");
try {
return execute(aopAllianceInvoker, target, method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}
}
具體看CacheAspectSupport
的execute()方法:
- 先拿到targetClasss
- 再拿到CacheOperationSource,這個(gè)類hold了注解
@Cacheable
,@CachePut
,@CacheEvict
的定義囊陡,即:Collection<CacheOperation> operations撞反。 - 調(diào)用第二個(gè)execute()方法鳍侣。
@Nullable
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);
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
第二個(gè)execute()方法:
- Synchronized case略過续扔,看常規(guī)的case雕拼。
- 當(dāng)注解為
@Cacheable
時(shí)胡诗,查詢cacheHit肴颊,不為空表示有緩存存在祟身。如果為空担神,需要放入cache待寫的list中(即:cachePutRequests
)躬窜。 - 然后就是進(jìn)行判斷:
- 如果
cacheHit
不為空并且不是@CachePut
操作这溅,那么從緩存里拿到cacheValue胳徽。 - 如果
cacheHit
為空虎锚,則執(zhí)行真正的目標(biāo)方法。
- 如果
- 如果為
@CachePut
操作薄货,那么也需要放入cache待寫的list中(即:cachePutRequests
)翁都。 - 讀取
cachePutRequests
,執(zhí)行cache寫操作谅猾。 - 對
@CacheEvict
注解進(jìn)行清理cache操作柄慰。
@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
// 略
}
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new ArrayList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && !hasCachePut(contexts)) {
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
上述CacheAspectSupport
類,execute()
方法中的從緩存里拿數(shù)據(jù)税娜,用的方法是:findCachedItem(contexts)
坐搔,具體源碼:
可以看到findCachedItem(contexts)
調(diào)用了--> findInCaches(context, key)
--> 調(diào)用了doGet(cache, key)
,而doGet方法位于CacheAspectSupport的
父類AbstractCacheInvoker
中敬矩,該抽象類提供基礎(chǔ)的doGet概行,doPut,doEvict方法弧岳。
@Nullable
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
for (CacheOperationContext context : contexts) {
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
Cache.ValueWrapper cached = findInCaches(context, key);
if (cached != null) {
return cached;
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
}
@Nullable
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
for (Cache cache : context.getCaches()) {
Cache.ValueWrapper wrapper = doGet(cache, key);
if (wrapper != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}
return wrapper;
}
}
return null;
}
【小結(jié)】【參考】