【Spring Cache】四 CacheOperation CacheOperationSource CacheAnnotationParser
前言
本章节了解 CacheOperation
CacheOperationSource
CacheAnnotationParser
相关内容
CacheOperation
public abstract class CacheOperation implements BasicOperation {
private final String name;
private final Set<String> cacheNames;
private final String key;
private final String keyGenerator;
private final String cacheManager;
private final String cacheResolver;
private final String condition;
private final String toString;
// ...
}
缓存操作的封装,具体的属性跟之前了解的缓存操作注解是一一对应的,提供了统一的 Builder
抽象
CacheableOperation
对应@Cacheable
注解CacheEvictOperation
对应@CacheEvict
注解CachePutOperation
对应@CachePut
注解
CacheOperationSource
// 基于 配置、元数据属性、类等解析缓存操作属性
public interface CacheOperationSource {
/**
* 判断是否需要对指定类每个方法进行内省来解析 CacheOperation
* 此处相当于对不受影响方法的一个优化避免内省
*/
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
// 获取目标类方法对应的 CacheOperation
@Nullable
Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);
}
- 顶层接口,主要用于解析给定方法对应的
CacheOperation
,可能基于配置
元数据信息
类层级信息
等 isCandidateClass
,相当于快速推断当前类是否需要对每个方法进行解析,节省性能开销,默认true
getCacheOperations
就是解析指定类方法的CacheOperation
集合了
NameMatchCacheOperationSource
public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable {
/**
* k:method names,支持正则匹配
* v:CacheOperation 集合
*/
private final Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
// 指定 nameMap
public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) {
nameMap.forEach(this::addCacheMethod);
}
// 给 nameMap 添加新的 kv
public void addCacheMethod(String methodName, Collection<CacheOperation> ops) {
this.nameMap.put(methodName, ops);
}
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
// 快速匹配
String methodName = method.getName();
Collection<CacheOperation> ops = this.nameMap.get(methodName);
// 如果没匹配就以正则形式再匹配一次
if (ops == null) {
// Look for most specific name match.
String bestNameMatch = null;
for (String mappedName : this.nameMap.keySet()) {
if (isMatch(methodName, mappedName)
&& (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
ops = this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}
return ops;
}
// method name 支持正则匹配
protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
// ...
}
- 这个实现类就是基于方法名称来获取对应的
CacheOperation
s,支持指定和条件对应的关系 - 方法名支持正则匹配
demo
public class NameMatchCacheOperationSourceDemo {
public static class TargetClass {
public void test() {}
public void test1() {}
}
@Test
public void test() throws NoSuchMethodException {
NameMatchCacheOperationSource cacheOperationSource
= new NameMatchCacheOperationSource();
// 指定方法名的正则及对应的 CacheOperation 集合
cacheOperationSource.addCacheMethod(
"test*"
, new ArrayList<CacheOperation>() {{
add(new CacheableOperation(new CacheableOperation.Builder()));
}}
);
// 获取指定方法对应的 CacheOperation 集合
Collection<CacheOperation> test = cacheOperationSource.getCacheOperations(
TargetClass.class.getMethod("test")
, null
);
Collection<CacheOperation> test1 = cacheOperationSource.getCacheOperations(
TargetClass.class.getMethod("test1")
, null
);
test.forEach(System.out::println);
test1.forEach(System.out::println);
}
}
结合示例理解 NameMatchCacheOperationSource
的用法
AbstractFallbackCacheOperationSource
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
// ...
// CacheOperations 的缓存
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Object 方法返回 null
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 缓存 key
Object cacheKey = getCacheKey(method, targetClass);
// 先从缓存获取
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
// 获取到就返回
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
// 缓存中不存在就由方法 computeCacheOperations 获取
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
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) {
/**
* 只允许 public 方法时忽略 non-public 方法
*/
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
/**
* 传进来的方法如果是接口方法,先用它的实现类方法获取
*/
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 第一次尝试从实现类的方法上获取
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}
// 第二次尝试从实现类上获取
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
// 再依此尝试从接口方法和接口上获取
if (specificMethod != method) {
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
return null;
}
// 子类实现,获取类上的 CacheOperation 集合
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
// 子类实现,获取方法上的 CacheOperation 集合
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
// 是否只解析 public 方法
protected boolean allowPublicMethodsOnly() {
return false;
}
}
AbstractFallbackCacheOperationSource
作为核心抽象基类,拓展了以下能力:
- 提供了
attributeCache
缓存,避免对同一方法多次解析 - 定义了整体的解析流程:
- 先从实现类方法上解析
- 其次从实现类上解析
- 再其次从接口类方法上解析
- 最后从接口类上解析
- 具体的从方法和类上解析
CacheOperation
的方法findCacheOperations
由子类实现
AnnotationCacheOperationSource
/**
* 基于注解元数据解析 CacheOperationSource
* 它主要基于 Spring 提供 Cacheable CachePut CacheEvict 注解的解析
* 并暴露相关的缓存操作定义
*/
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
// 支持指定是否只解析 public 方法
private final boolean publicMethodsOnly;
private final Set<CacheAnnotationParser> annotationParsers;
// 默认只解析 public 方法
public AnnotationCacheOperationSource() {
this(true);
}
// 默认缓存注解解析类为 SpringCacheAnnotationParser
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
// ...
/**
* 委托给 CacheAnnotationParser#isCandidateClass
* 有一个通过就行
*/
@Override
public boolean isCandidateClass(Class<?> targetClass) {
for (CacheAnnotationParser parser : this.annotationParsers) {
if (parser.isCandidateClass(targetClass)) {
return true;
}
}
return false;
}
/**
* 基于 CacheAnnotationParser 解析指定类上的 CacheOperation
* 集合并合并返回
*/
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
/**
* 基于 CacheAnnotationParser 解析指定方法上的 CacheOperation
* 集合并合并返回
*/
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
// ...
}
AbstractFallbackCacheOperationSource
的实现,就是基于注解解析对应的 CacheOperation
:
- 支持指定是否只解析
public
方法,默认true
- 具体的解析行为其实都是委托
CacheAnnotationParser
实现,默认实例SpringCacheAnnotationParser
,后文会具体了解 isCandidateClass
委托CacheAnnotationParser#isCandidateClass
findCacheOperations
的实现也是委托CacheAnnotationParser#parseCacheAnnotations
,最后合并返回
CompositeCacheOperationSource
Spring
常用的设计模式 —— 组合模式
,其下维护一组 CacheOperationSource
,具体的方法实现自然也是委托下去的,细节略
CacheAnnotationParser
/**
* 这是解析缓存注解的策略接口
* 基于这个接口我们就可以拓展对指定注解的解析
* 比如针对 Spring 提供注解的解析
*/
public interface CacheAnnotationParser {
// 推断是否需要对指定类解析内省,这相当于对不受影响方法的一个优化
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
// 解析指定类的 CacheOperation 集合
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Class<?> type);
// 解析指定方法的 CacheOperation 集合
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Method method);
}
- 前文也了解到了,它是辅助
AnnotationCacheOperationSource
基于注解解析CacheOperation
s 的策略接口 - 可以看到它的方法与
AnnotationCacheOperationSource
是一一对应的:isCandidateClass
快速检查目标类是否需要对所有方法进行解析parseCacheAnnotations(Class<?> type)
获取目标类上的CacheOperation
sparseCacheAnnotations(Method method)
获取目标方法上的CacheOperation
s
SpringCacheAnnotationParser
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
// ...
// Spring 对应的 CacheOperation 对应注解
static {
CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class);
CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class);
CACHE_OPERATION_ANNOTATIONS.add(CachePut.class);
CACHE_OPERATION_ANNOTATIONS.add(Caching.class);
}
// 指定类上、类的方法、类的属性上是否包含指定注解
@Override
public boolean isCandidateClass(Class<?> targetClass) {
return AnnotationUtils.isCandidateClass(targetClass, CACHE_OPERATION_ANNOTATIONS);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
// 基于 @CacheConfig 注解生成的默认配置
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
// 基于 @CacheConfig 注解生成的默认配置
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
/**
* 先忽略层级,如果解析的 CacheOperation 多余 1 个
* 则在本层级再解析一次
* (应该是为了防止接口和实现类都声明注解导致重复)
*/
Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
if (ops != null && ops.size() > 1) {
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;
}
/**
* 把所有注解元素封装成对应的 CacheOperation
* @Cacheable -> CacheableOperation
* @CacheEvict -> CacheEvictOperation
* @CachePut -> CachePutOperation
* @Caching -> 分别解析成上述 CacheOperation
*/
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;
}
// 校验 CacheOperation
private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
// 不能同时指定 key 和 keyGenerator
if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
throw new IllegalStateException("...");
}
// 不能同时指定 cacheManager 和 cacheResolver
if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
throw new IllegalStateException("...");
}
}
// ...
}
Spring
提供的唯一实现,针对的自然就是 Spring
定义的缓存操作注解 @Cacheable
@CacheEvict
@CachePut
@Caching
,其中:
isCandidateClass
直接判断当前类及其方法、属性等元素是否包含上述注解即可parseCacheAnnotations
方法的实现大致概括就是获取目标元素所有层级上(当CacheOperation
多于一个时只解析本层级)所有相关注解,然后:@Cacheable
注解解析为CacheableOperation
@CacheEvict
注解解析为CacheEvictOperation
@CachePut
注解解析为CachePutOperation
@Caching
注解包含一组缓存注解,依此将它们进行上述解析
validateCacheOperation
方法是对解析后的CacheOperation
进行校验,比如不能同时指定key
和keyGenerator
、cacheManager
和cacheResolver
demo
整体上个 demo
,AnnotationCacheOperationSource
基于注解解析 CacheOperation
public class CacheOperationSourceDemo {
public static class CacheTarget {
@Cacheable
public String a(String a) {
return "a";
}
@CachePut
public String b(String b) {
return "b";
}
}
@Test
public void test() throws NoSuchMethodException {
AnnotationCacheOperationSource cacheOperationSource
= new AnnotationCacheOperationSource();
boolean candidateClass =
cacheOperationSource.isCandidateClass(CacheTarget.class);
System.out.println(candidateClass);
Collection<CacheOperation> a = cacheOperationSource.getCacheOperations(
CacheTarget.class.getMethod("a", String.class)
, null
);
a.forEach(System.out::println);
}
}
总结
CacheOperation
,对应缓存操作注解:@Cacheable -> CacheableOperation
@CacheEvict -> CacheEvictOperation
@CachePut -> CachePutOperation
CacheOperationSource
,用于从目标类方法上解析上述CacheOperation
,子类AnnotationCacheOperationSource
基于对应注解解析CacheAnnotationParser
, 解析缓存操作注解对应的CacheOperation
,AnnotationCacheOperationSource
对注解的解析等方法都委托其子类SpringCacheAnnotationParser
实现的
上一篇:【Spring Cache】三 CacheOperationInvocationContext CacheOperationExpressionEvaluator