【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 抽象

CacheOperation

  • 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);
	}

	// ...

}
  • 这个实现类就是基于方法名称来获取对应的 CacheOperations,支持指定和条件对应的关系
  • 方法名支持正则匹配

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 基于注解解析 CacheOperations 的策略接口
  • 可以看到它的方法与 AnnotationCacheOperationSource 是一一对应的:
    • isCandidateClass 快速检查目标类是否需要对所有方法进行解析
    • parseCacheAnnotations(Class<?> type) 获取目标类上的 CacheOperations
    • parseCacheAnnotations(Method method) 获取目标方法上的 CacheOperations

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 进行校验,比如不能同时指定 keykeyGeneratorcacheManagercacheResolver

demo

整体上个 demoAnnotationCacheOperationSource 基于注解解析 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, 解析缓存操作注解对应的 CacheOperationAnnotationCacheOperationSource 对注解的解析等方法都委托其子类 SpringCacheAnnotationParser 实现的

上一篇:【Spring Cache】三 CacheOperationInvocationContext CacheOperationExpressionEvaluator

下一篇:【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值