上一章最后留了关于表达式 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 | protected Expression getExpression(Map<ExpressionKey, Expression> cache, |
尝试从 cache 获取表达式,不存在则由 parser 执行 parseExpression 并将结果缓存起来。
CacheOperationExpressionEvaluator 的相关数据结构
CacheExpressionRootObject
1 | private final Collection<? extends Cache> caches; |
CacheEvaluationContext
CacheEvaluationContext 继承自 MethodBasedEvaluationContext,在其基础上对不可用变量 unavailableVariables
额外做了一级缓存。
关于 MethodBasedEvaluationContext 的实现,是 SpEL 的部分,此处不展开。简单来说其将方法的参数注入上下文,提供了一个 lookupVariable 方法用于从上下文查找变量。
CacheOperationExpressionEvaluator 核心源码
1 | public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches, |
用 new 出来的 CacheExpressionRootObject 作为 rootObject,带上当前的方法和参数一起生成了一个 CacheEvaluationContext。
其提供了三处需要进行 SpEL 求值的函数:
1 | public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) { |
最终由 generateKey 等方法调用,由 SpEL 表达式结合上下文计算出实际值。
总结
到这章为止,Spring Cache 的核心源码基本讲完了。
Spring cache 通过 ProxyCachingConfiguration 配置。由 AnnotationCacheOperationSource 使用 SpringCacheAnnotationParser 将注解解析为 CacheOperation 并缓存。
CacheOperationSourcePointcut 通过尝试使用 AnnotationCacheOperationSource 取 CacheOperation 来判断是否需要进入切面。
CacheAspectSupport 作为拦截类,使用 AnnotationCacheOperationSource 获取到 CacheOperation,然后依赖 CacheResolver 等进行 Cache 解析,结合 CacheOperationExpressionEvaluator 进行表达式求值,最终执行 cache 的增删查操作。