SpringCache源码| Spring Cache的AOP实现原理

1. 简述

Spring Cache是通过AOP面向切面编程的思想来对缓存的操作进行封装,通过拦截器对实现了Spring Cache注解的方法进行拦截,可以根据注解信息去完成相应的缓存操作。

2. CacheInterceptor拦截器

Spring Cache提供了一个缓存拦截器,负责拦截方法调用执行缓存逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
public CacheInterceptor() {
}

//被拦截的方法都会走invoke
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
//invocation就是一个连接点(Joinpoint),其实就是对方调用方法的封装。
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
return invocation.proceed();
} catch (Throwable var2) {
throw new ThrowableWrapper(var2);
}
};

try {
//核心的方法,来自于父类
return this.execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
} catch (ThrowableWrapper var5) {
throw var5.getOriginal();
}
}
}

CacheIntercepto实现了MethodInterceptor,MethodInterceptor主要对方法进行拦截,查看MethodInterceptor的继承图,发现MethodInterceptor就是一个Advice,在Spring AOP的概念里面,Advice就是一个通知,通知封装了AOP的横切逻辑。由此来说CacheInterceptor作为一个通知,他里面肯定包含缓存操作的横切逻辑,能够处理方法调用前后的缓存操作。

2.1 查看父类的核心方法execute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
//是否初始化成功
if(this.initialized) {
Class<?> targetClass = this.getTargetClass(target);
//获取CacheOperation资源类,负责解析Spring Cache注解的类
CacheOperationSource cacheOperationSource = this.getCacheOperationSource();
if(cacheOperationSource != null) {
//解析注解,得到CacheOperation的集合。
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
if(!CollectionUtils.isEmpty(operations)) {
//调用内部方法execute,封装了一个缓存操作的上下文并传入
return this.execute(invoker, method, new CacheAspectSupport.CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}

return invoker.invoke();
}

这个方法的主要作用:

  1. 获取缓存操作集合CacheOperation
  2. 封装缓存操作上下文,并调用内部的execute方法

2.2 核心内部方法execute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@Nullable
private Object execute(CacheOperationInvoker invoker, Method method, CacheAspectSupport.CacheOperationContexts contexts) {
if(contexts.isSynchronized()) {
//获取CacheableOperation对应的缓存操作上下文。
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)contexts.get(CacheableOperation.class).iterator().next();
if(this.isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
Object key = this.generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
//获取缓存对象
Cache cache = (Cache)context.getCaches().iterator().next();

try {

return this.wrapCacheValue(method, cache.get(key, () -> {
return this.unwrapReturnValue(this.invokeOperation(invoker));
}));
} catch (ValueRetrievalException var10) {
throw (ThrowableWrapper)var10.getCause();
}
} else {
return this.invokeOperation(invoker);
}
} else {
//1. 处理BeforeInvocation = true的缓存删除操作
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT);
//2. 查找是否有@Cacheable的缓存数据
ValueWrapper cacheHit = this.findCachedItem(contexts.get(CacheableOperation.class));
List<CacheAspectSupport.CachePutRequest> cachePutRequests = new LinkedList();
if(cacheHit == null) {
//如果没有缓存数据,则封装成CachePutRequest
this.collectPutRequests(contexts.get(CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}

Object cacheValue;
Object returnValue;

if(cacheHit != null && !this.hasCachePut(contexts)) {
//获取缓存数据里面的值
cacheValue = cacheHit.get();
returnValue = this.wrapCacheValue(method, cacheValue);
} else {
//如果没有找到Cacheable的缓存数据,或者缓存注解是CachePut则调用方法,获取方法返回值
returnValue = this.invokeOperation(invoker);
cacheValue = this.unwrapReturnValue(returnValue);
}

//3. 将CachePut注解封装成CachePutRequest
this.collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
Iterator var8 = cachePutRequests.iterator();

while(var8.hasNext()) {
//遍历cachePutRequest,将方法调用的返回值置入缓存中。
CacheAspectSupport.CachePutRequest cachePutRequest = (CacheAspectSupport.CachePutRequest)var8.next();
cachePutRequest.apply(cacheValue);
}

//处理BeforeInvocation = false的缓存删除操作
this.processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
}

如果不需要同步,该方法主要完成了以下的逻辑

  1. 处理BeforeInvocation = true的缓存删除操作
  2. 通过@Cacheable注解找到对应的缓存值。
  3. 如果没有找到对应的缓存数据,则将@Cacheable封装成CachePutRequest,标记为一个插入缓存的请求。
  4. 进行方法调用
  5. 将CachePut注解封装成CachePutRequest
  6. 遍历CachePutReqeust集合,将方法调用的返回值置入缓存中。
  7. 处理BeforeInvocation = false的缓存删除操作

梳理一下,这个方法里面包括三个注解的逻辑

  1. @Cacheable
    (1) 首先从缓存中找@Cacheable对应的缓存
    (2) 如果缓存存在,则取出缓存值作为返回值
    (3) 如果缓存不存在,则进行方法调用,获取返回值,并将返回值置入缓存

  2. @CachePut
    (1) 如果包含cachePut注解,则进行方法调用。
    (2) 获取方法返回值,并置入缓存之中。

  3. @CacheEvic
    (1) 在方法调用签执行缓存删除操作。
    (2) 在方法调用后执行缓存删除操作。

2.2.1 processCacheEvicts

我们来看看缓存删除的具体逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//beforeInvocation指的是方法调用前还是调用后,@CacheEvict可以进行设置
private void processCacheEvicts(Collection<CacheAspectSupport.CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
Iterator var4 = contexts.iterator();

while(var4.hasNext()) {
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)var4.next();
CacheEvictOperation operation = (CacheEvictOperation)context.metadata.operation;
if(beforeInvocation == operation.isBeforeInvocation() && this.isConditionPassing(context, result)) {
//处理缓存删除操作
this.performCacheEvict(context, operation, result);
}
}

}

private void performCacheEvict(CacheAspectSupport.CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
Object key = null;
Iterator var5 = context.getCaches().iterator();

while(var5.hasNext()) {
//遍历获取Cache对象
Cache cache = (Cache)var5.next();
if(operation.isCacheWide()) {
this.logInvalidating(context, operation, (Object)null);
//删除全部缓存
this.doClear(cache);
} else {
if(key == null) {
key = this.generateKey(context, result);
}

this.logInvalidating(context, operation, key);
//删除指定key的缓存
this.doEvict(cache, key);
}
}

}

2.2.2 findCachedItem(获取缓存数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@Nullable
private ValueWrapper findCachedItem(Collection<CacheAspectSupport.CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
Iterator var3 = contexts.iterator();

while(var3.hasNext()) {
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)var3.next();
if(this.isConditionPassing(context, result)) {
//生成相应的Cache key
Object key = this.generateKey(context, result);
//通过缓存上下文和key找寻相应的缓存包装对象
ValueWrapper cached = this.findInCaches(context, key);
if(cached != null) {
return cached;
}

if(this.logger.isTraceEnabled()) {
this.logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}

return null;
}

@Nullable
private ValueWrapper findInCaches(CacheAspectSupport.CacheOperationContext context, Object key) {
Iterator var3 = context.getCaches().iterator();

Cache cache;
ValueWrapper wrapper;
do {
if(!var3.hasNext()) {
return null;
}
//获取缓存对象,并通过key去查找缓存值
cache = (Cache)var3.next();
wrapper = this.doGet(cache, key);
} while(wrapper == null);

if(this.logger.isTraceEnabled()) {
this.logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}

return wrapper;
}

2.2.3collectPutRequests(封装存储缓存的请求)

1
2
3
4
5
6
7
8
9
10
11
12
13
private void collectPutRequests(Collection<CacheAspectSupport.CacheOperationContext> contexts, @Nullable Object result, Collection<CacheAspectSupport.CachePutRequest> putRequests) {
Iterator var4 = contexts.iterator();

while(var4.hasNext()) {
CacheAspectSupport.CacheOperationContext context = (CacheAspectSupport.CacheOperationContext)var4.next();
if(this.isConditionPassing(context, result)) {
Object key = this.generateKey(context, result);
//通过key和缓存上下文封装一个插入缓存的请求。
putRequests.add(new CacheAspectSupport.CachePutRequest(context, key));
}
}

}

3 总结

目前,关于Spring Cache的源码分析就告一段落了,总的来说,SpringCache的分析文章主要对缓存注解实现的源码做了一个简要的分析,目的是能够对缓存操作的AOP实现原理有一个清晰的认识。能够搞清楚Spring Cache是如何借助AOP的思想来实现如何简单、快捷且代码侵入低的缓存组件。由此我们可以借鉴这些思想和案例来丰富我们的阅历,以助于提高我们的技术水平,帮助我们能够实现类似的开源组件。