Spring Cache (6) - CacheOperationExpressionEvaluator 中的表达式求值

上一章最后留了关于表达式 Evaluation 的部分没有讲,这期细讲。

Cache 注解的 key、unless、condition 等都是支持 SpEL 的。而对这块的支持是在 CacheOperationExpressionEvaluator 中实现的。

首先可以看到 CacheAspectSupport 中有一个 evaluator 变量,其定义如下:

1
private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();

接下来我们详细分析这个 CacheOperationExpressionEvaluator

最近工作有点忙,再次拖更,之后忙完这阵会补回来的。

CacheOperationExpressionEvaluator

基类 CachedExpressionEvaluator

定义了一个 protected 的静态内部类 ExpressionKey,实现了 Comparable 接口,包含 element(类型为 AnnotatedElementKey)和 expression(类型为 String)两个字段。

定义了一个 parser ,由父类继承构造的时候赋值了一个 new SpelExpressionParser()。关于 SpelExpressionParser 此处不展开。

定义了一个 parameterNameDiscoverer,赋值为一个 new DefaultParameterNameDiscoverer()

关于 DefaultParameterNameDiscoverer,是为了兼容低于 Java 8 的版本。在 Java 8 之前是没有反射的,使用的是 LocalVariableTableParameterNameDiscoverer,内部依赖 ASM 库实现(详见其 inspectClass 方法,此处不展开);Java 8 以及之后使用的是 StandardReflectionParameterNameDiscoverer,直接使用反射即可。

CachedExpressionEvaluator 的关键代码只有一段:

1
2
3
4
5
6
7
8
9
10
protected Expression getExpression(Map<ExpressionKey, Expression> cache,
AnnotatedElementKey elementKey, String expression) {
ExpressionKey expressionKey = createKey(elementKey, expression);
Expression expr = cache.get(expressionKey);
if (expr == null) {
expr = getParser().parseExpression(expression);
cache.put(expressionKey, expr);
}
return expr;
}

尝试从 cache 获取表达式,不存在则由 parser 执行 parseExpression 并将结果缓存起来。

CacheOperationExpressionEvaluator 的相关数据结构

CacheExpressionRootObject

1
2
3
4
5
6
7
8
9
private final Collection<? extends Cache> caches;

private final Method method;

private final Object[] args;

private final Object target;

private final Class<?> targetClass;

CacheEvaluationContext

CacheEvaluationContext 继承自 MethodBasedEvaluationContext,在其基础上对不可用变量 unavailableVariables 额外做了一级缓存。

关于 MethodBasedEvaluationContext 的实现,是 SpEL 的部分,此处不展开。简单来说其将方法的参数注入上下文,提供了一个 lookupVariable 方法用于从上下文查找变量。

CacheOperationExpressionEvaluator 核心源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass, Object result,
BeanFactory beanFactory) {

CacheExpressionRootObject rootObject = new CacheExpressionRootObject(
caches, method, args, target, targetClass);
Method targetMethod = getTargetMethod(targetClass, method);
CacheEvaluationContext evaluationContext = new CacheEvaluationContext(
rootObject, targetMethod, args, getParameterNameDiscoverer());
if (result == RESULT_UNAVAILABLE) {
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
}
else if (result != NO_RESULT) {
evaluationContext.setVariable(RESULT_VARIABLE, result);
}
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}

用 new 出来的 CacheExpressionRootObject 作为 rootObject,带上当前的方法和参数一起生成了一个 CacheEvaluationContext。

其提供了三处需要进行 SpEL 求值的函数:

1
2
3
4
5
6
7
8
9
10
11
public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return getExpression(this.keyCache, methodKey, keyExpression).getValue(evalContext);
}

public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return getExpression(this.conditionCache, methodKey, conditionExpression).getValue(evalContext, boolean.class);
}

public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return getExpression(this.unlessCache, methodKey, unlessExpression).getValue(evalContext, boolean.class);
}

最终由 generateKey 等方法调用,由 SpEL 表达式结合上下文计算出实际值。

总结

到这章为止,Spring Cache 的核心源码基本讲完了。

Spring cache 通过 ProxyCachingConfiguration 配置。由 AnnotationCacheOperationSource 使用 SpringCacheAnnotationParser 将注解解析为 CacheOperation 并缓存。

CacheOperationSourcePointcut 通过尝试使用 AnnotationCacheOperationSource 取 CacheOperation 来判断是否需要进入切面。

CacheAspectSupport 作为拦截类,使用 AnnotationCacheOperationSource 获取到 CacheOperation,然后依赖 CacheResolver 等进行 Cache 解析,结合 CacheOperationExpressionEvaluator 进行表达式求值,最终执行 cache 的增删查操作。