spring cache 2: CacheOperationSource


spring 启动过程中就会对一个被Cache相关注解类修饰的类进行解析,解析主要用到了 CacheOperationSource 相关类

在这里插入图片描述

uml

在这里插入图片描述

CacheOperationSource

CacheOperationSource 接口定义了如何通过Class和Method获取当前类的方法上,涉及到cache相关操作的信息,接口只有1个方法:

	/**
	 * Return the collection of cache operations for this method,
	 * or {@code null} if the method contains no <em>cacheable</em> annotations.
	 * @param method the method to introspect
	 * @param targetClass the target class (may be {@code null}, in which case
	 * the declaring class of the method must be used)
	 * @return all cache operations for this method, or {@code null} if none found
	 */
	@Nullable
	Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);

AbstractFallbackCacheOperationSource

抽象类AbstractFallbackCacheOperationSource继承自CacheOperationSource

私有字段

内部私有字段attributeCache对类中的方法进行了缓存,可以避免重复计算CacheOperation
反射操作比较耗时,对反射接口进行内部缓存

需要子类实现的方法

/**
 * 根据 Class 计算出 CacheOperation
 */
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);

/**
 * 根据 Method 计算出 CacheOperation
 */
protected abstract Collection<CacheOperation> findCacheOperations(Method method);

getCacheOperations() 的实现

抽象类对 getCacheOperations() 方法进行了实现

  1. 根据 Class 和 Method 生成从缓存attributeCache中get操作的key,key实际类型是 MethodClassKey
  2. 从缓存中获取当前类方法可能涉及到的缓存操作信息CacheOperation集合
    3.缓存命中,返回结果;缓存不命中,计算当前类方法可能涉及到的缓存操作信息CacheOperation集合,把结果放在缓存中并返回结果
    public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return null;
		}
        // 根据 Class 和 Method 生成从缓存attributeCache中get操作的key,key实际类型是 MethodClassKey
		Object cacheKey = getCacheKey(method, targetClass);
        // 从缓存中获取当前类方法可能涉及到的缓存操作信息CacheOperation集合
		Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);

		if (cached != null) {
            // 缓存命中,返回结果
			return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
		}
		else {
            // 缓存不命中,计算当前类方法可能涉及到的缓存操作信息CacheOperation集合,并把结果放在缓存中
			Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
			if (cacheOps != null) {
				if (logger.isTraceEnabled()) {
					logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
				}
				this.attributeCache.put(cacheKey, cacheOps);
			}
			else {
				this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
			}
			return cacheOps;
		}
	}

计算 CacheOperation 集合的操作在私有方式 computeCacheOperations 中

CacheAnnotationParser 相关类

因为 AnnotationCacheOperationSource 主要用到 CacheAnnotationParser 及相关类进行操作,看 AnnotationCacheOperationSource 之前先了解下 CacheAnnotationParser 及其相关类

CacheAnnotationParser

CacheAnnotationParser 接口定义了从Class和Method中获取 CacheOperation 的方法


public interface CacheAnnotationParser {
	/**
	 * 从 Class 中获取 CacheOperation
	 */
	@Nullable
	Collection<CacheOperation> parseCacheAnnotations(Class<?> type);

	/**
	 * 从 Method 中获取 CacheOperation
	 */
	@Nullable
	Collection<CacheOperation> parseCacheAnnotations(Method method);

}

SpringCacheAnnotationParser

CacheAnnotationParser 目前只有一个实现类 SpringCacheAnnotationParser, 先了解下 SpringCacheAnnotationParser 的内部类 DefaultCacheConfig

SpringCacheAnnotationParser.DefaultCacheConfig

SpringCacheAnnotationParser.DefaultCacheConfig 这个内部类,用来解析 @CacheConfig 注解的,@CacheConfig 这个注解用在类上,用来简化一些一模一样的配置信息。

  • SpringCacheAnnotationParser.DefaultCacheConfig 私有字段 cacheNames,keyGenerator,cacheManager,cacheResolver 就是 @CacheConfig 注解中的字段。
  • Class<?> target; 表示当前的Class。
  • initialized 表示当前操作的 DefaultCacheConfig 对象是否需要读取 target 上的 @CacheConfig 信息。new 一个 DefaultCacheConfig 对象时,initialized=false

applyDefault() 方法:

  1. 如果 initialized = false(DefaultCacheConfig 刚创建的),
    会读取Class上的@CacheConfig注解,并解析出cacheNames,keyGenerator,cacheManager,cacheResolver字段,并设置initialized=true
  2. 方法上的的@Cahceable,@CachePut,@CacheEvict注解中,如果配置类这4个字段中的任何一个未设置,用类上的配置进行替代。
        /**
         * @param builder 从 @Cacheable或者@CahcePut或者@CacheEvict中解析出来的的字段
         */
		public void applyDefault(CacheOperation.Builder builder) {
			if (!this.initialized) {
				CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, CacheConfig.class);
				if (annotation != null) {
					this.cacheNames = annotation.cacheNames();
					this.keyGenerator = annotation.keyGenerator();
					this.cacheManager = annotation.cacheManager();
					this.cacheResolver = annotation.cacheResolver();
				}
				this.initialized = true;
			}

            // 如果注解在方法上未设置这些字段,用注解在类上的字段去填充
			if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
				builder.setCacheNames(this.cacheNames);
			}
			if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) &&
					StringUtils.hasText(this.keyGenerator)) {
				builder.setKeyGenerator(this.keyGenerator);
			}

			if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
                // cacheManager和cacheResolver 是互斥字段,不可能同时有;一个字段设置了,那么这两个字段都不需要覆盖
			}
			else if (StringUtils.hasText(this.cacheResolver)) {
				builder.setCacheResolver(this.cacheResolver);
			}
			else if (StringUtils.hasText(this.cacheManager)) {
				builder.setCacheManager(this.cacheManager);
			}
		}
	}

私有字段

SpringCacheAnnotationParser 开头定义了它能解析的4个注解:

private static final Set<Class<? extends Annotation>> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8);
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);
}

@Cacheable,@CachePut,@CacheEvict 注解用在方法上
@Caching 是涉及到 @Cacheable,@CachePut,@CacheEvict 3个注解的一个或者多个的组合

解析 CacheOperation

因为 @Cacheable,@CahcePut, @CacheEvict,@Caching 这些注解只能用在方法上,因此这里只解释 public Collection parseCacheAnnotations(Method method)

核心逻辑在私有方法中:
private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly)
收集Method上的注解 @Cacheable, @CachePut, @CacheEvict, @Caching然后转成 CacheOperation

这里在阅读的过程中有一点疑问:

private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) 方法中有如下判断:当CacheOperation的数量大于1时,会有另一次解析,也就是说
private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) 这个私有方法中的localOnly有什么作用?

    private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
		Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
		if (ops != null && ops.size() > 1) {
            // 当查到的 CacheOperation 数量大于1时,有如下逻辑:为什么
			Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
			if (localOps != null) {
				return localOps;
			}
		}
		return ops;
	}

AnnotationCacheOperationSource

AnnotationCacheOperationSource 获取 CacheOperation 是通过私有方法 determineCacheOperations() 实现的

CacheOperationProvider()方法的参数类型是 CacheOperationProvider,其实实现就是 SpringCacheAnnotationParser。内部逻辑比较简单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值