Spring-Cache源码:基于注解的缓存

1. 使用EnableCaching注解引入组件

根据Spring的官网得知,使用Spring-Cache的第一步就是要标注@EnableCaching注解

@Import(CachingConfigurationSelector.class)
public @interface EnableCaching { /***/ }

EnableCaching注解使用@Import注解导入了CachingConfigurationSelector

在使用Spring时,这是一种非常优雅且见名知意的功能打开方式
如果在日常开发中,开发了某一类功能,也可以借鉴Spring的这种方式
自定义@Enablexxx注解配合@Import注解

2. CachingConfigurationSelector

CachingConfigurationSelector继承了AdviceModeImportSelector
覆写了selectImports方法,该方法返回的String[]的类全名会被Spring注册到容器中

2.1 selectImports
	public String[] selectImports(AdviceMode adviceMode) {
		switch (adviceMode) {
			case PROXY:
				return getProxyImports();
			case ASPECTJ:
				return getAspectJImports();
			default:
				return null;
		}
	}

绝大部分的场景都是使用基于代理的模式,而不是织入的模式,所以继续跟踪getProxyImports()

2.2 selectImports
	private String[] getProxyImports() {
		List<String> result = new ArrayList<>(3);
		
		// 1. 该类用于创建Spring代理
		result.add(AutoProxyRegistrar.class.getName());
		
		// 2. 核心类,Spring-Cache配置类
		result.add(ProxyCachingConfiguration.class.getName());
		
		// 3. 忽略,用于处理javax.cache.annotation包下的缓存注解的
		// 本次源码只分析Spring提供的缓存注解
		if (jsr107Present && jcacheImplPresent) {
			result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
		}
		
		return StringUtils.toStringArray(result);
	}
  1. AutoProxyRegistrar最终会导入AbstractAutoProxyCreator
    用于介入bean的生命周期,创建代理对象的,属于Spring-AOP相关内容
    本章只谈论Spring-Cache的源码,Spring-AOP的源码可以看这篇文章Spring-AOP源码分析

  2. ProxyCachingConfiguration
    Spring-AOP的配置类,引入了Spring-AOP相关组件到Spring容器中

3. ProxyCachingConfiguration

ProxyCachingConfiguration定义了三个类

  1. CacheInterceptor
  2. CacheOperationSource
  3. BeanFactoryCacheOperationSourceAdvisor

其关系如下
在这里插入图片描述

BeanFactoryCacheOperationSourceAdvisor是一个Advisor

根据Spring-AOP的源码可知,在bean创建的过程中,会找到Spring容器中所有的Advisor并且判断是否可以被这个Bean使用

判断时则根据AdvisorpointcutClassFilterMethodFitler的匹配结果

就不具体去分析是否匹配的逻辑了,具体的表现为,只要bean的类或者方法上定义了
@Cacheable
@Cacheput
@CacheEvict
@Caching
任一注解,这个bean就会被代理

4. CacheInterceptor

根据Spring-AOP的源码可知,当一个类被创建代理后,该类执行方法前会执行MethodInterceptorinvoke方法

CacheInterceptor就是一个MethodInterceptor

	@Override
	@Nullable
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Method method = invocation.getMethod();

		CacheOperationInvoker aopAllianceInvoker = () -> {
			try {
				return invocation.proceed();
			}
			catch (Throwable ex) {
				throw new CacheOperationInvoker.ThrowableWrapper(ex);
			}
		};

		try {
			// 1. 第一个参数:一个invoker, 执行invoker的invoke()方法会调用invocation的invoke()方法
		
			// 2. 第二个参数:当前被代理的对象

			// 3. 第三个参数:当前正在执行的方法
			
			// 4. 第四个参数:当前正在执行的方法参数
			
			return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
		}
		catch (CacheOperationInvoker.ThrowableWrapper th) {
			throw th.getOriginal();
		}
	}

CacheOperationInvoker aopAllianceInvoker = () -> invocation.proceed()
好像什么也没有做,只是调用invocation的invoke方法,那Spring为什么要这样写呢
因为需要兼容不同的代理方式,例如aspectj织入

可以把aopAllianceInvoker当做invocation 即可,具有执行被代理方法的能力

4.1 execute
	@Nullable
	protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
		
		// 1. 获取执行对象的class
		Class<?> targetClass = getTargetClass(target);
			
		// 2. 获取CacheOperationSource, 就是在 { ProxyCachingConfiguration } 赋值给当前对象的 { CacheOperationSource }
		CacheOperationSource cacheOperationSource = getCacheOperationSource();
		
		// 3. 通过 { CacheOperationSource } 获取 { CacheOperation } 集合
		Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
		
		if (!CollectionUtils.isEmpty(operations)) {
			return execute(invoker, method,
					new CacheOperationContexts(operations, method, args, target, targetClass));
		}

		return invoker.invoke();
	}

5. AnnotationCacheOperationSource

5.1 getCacheOperations
	public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
		if (method.getDeclaringClass() == Object.class) {
			return null;
		}

		// 1. 构建缓存key
		Object cacheKey = getCacheKey(method, targetClass);
		Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);

		// 2. 如果有缓存
		if (cached != null) {
			return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
		}
	
		else {
			// 3. 否则调用 { computeCacheOperations } 方法
			Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
			this.attributeCache.put(cacheKey, cacheOps);
			return cacheOps;
		}
	}
5.2 computeCacheOperations
	@Nullable
	private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
		
		
		// 1. 获取最"具体"的方法 : 例如方法是定义在接口上, 或者抽象类上, 那么找到具体实现的方法对象
		Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

		// 2. 从方法上找是否有"缓存"注解
		Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
		if (opDef != null) {
			return opDef;
		}

		// 3. 和第二步的逻辑大同小异, 从类上是否有"缓存"注解
		opDef = findCacheOperations(specificMethod.getDeclaringClass());
		if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
			return opDef;
		}
		
		// 4. 找不到, 返回空
		return null;
	}
5.3 findCacheOperations
	protected Collection<CacheOperation> findCacheOperations(Method method) {
		return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
	}

findCacheOperations方法也没做啥事,而是委托给了parser

parser只有一个实现类,那就是SpringCacheAnnotationParser

5.4 SpringCacheAnnotationParser#parseCacheAnnotations
	@Nullable
	private Collection<CacheOperation> parseCacheAnnotations(
			DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
		
		// 1. 获取类上 | 方法上的所有注解
		Collection<? extends Annotation> anns = (localOnly ?
				AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
				AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
				
		if (anns.isEmpty()) {
			return null;
		}

		final Collection<CacheOperation> ops = new ArrayList<>(1);
		
		// 2. 遍历注解

		// 2.1 找到@Cacheable注解, 封装成 { CacheableOperation }, 放入 { ops } 集合中
		anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
				ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));

		// 2.2 找到@CacheEvict注解, 封装成 { CacheEvictOperation }, 放入 { ops } 集合中				
		anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
				ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));

		// 2.3 找到@CachePut注解, 封装成 { CachePutOperation }, 放入 { ops } 集合中					
		anns.stream().filter(ann -> ann instanceof CachePut).forEach(
				ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));

		// 2.4 找到@Caching注解, 放入 { ops } 集合中				
		// ps: Caching是多个 { @Cacheable }, { @CacheEvict }, { @CachePut }的集合
		anns.stream().filter(ann -> ann instanceof Caching).forEach(
				ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));

		return ops;
	}

在这里插入图片描述

此时,CacheOperation的查找过程就完成了

继续回到CacheInterceptor#execute方法

6. CacheInterceptor

6.1 execute
	@Nullable
	protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
		
		// 1. 获取执行对象的class
		Class<?> targetClass = getTargetClass(target);
			
		// 2. 获取CacheOperationSource, 就是在 { ProxyCachingConfiguration } 赋值给当前对象的 { CacheOperationSource }
		CacheOperationSource cacheOperationSource = getCacheOperationSource();
		
		// 3. 通过 { CacheOperationSource } 获取 { CacheOperation } 集合
		Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
		
		// 以上是4.1分析过的
		// =====================================================================
		
		// 4. 如果operations不为空, 那么调用重载的 { execute } 方法, 并且创建 { CacheOperationContexts }
		if (!CollectionUtils.isEmpty(operations)) {
			return execute(invoker, method,
					new CacheOperationContexts(operations, method, args, target, targetClass));
		}
		
		// 5. 如果为空, 证明当前执行的方法不需要执行增强, 直接执行被代理方法
		return invoker.invoke();
	}
6.2 CacheOperationContexts

在阅读重载的execute方法前, 先看下CacheOperationContexts的创建过程

		public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
				Object[] args, Object target, Class<?> targetClass) {
		
			// 1. 初始 { contexts } 集合对象
			this.contexts = new LinkedMultiValueMap<>(operations.size());
			
			// 2. 将 { CacheOperation } 封装为 { CacheOperationContext }
 			for (CacheOperation op : operations) {
				this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
			}
			
			// 3. 不常用, 忽略
			this.sync = determineSyncFlag(method);
		}

PS: 这里和Netty的ChannelHandlerContext有点相似

  • Netty是将Handler包装为ChannelHandlerContext
  • Spring将CacheOperation包装为CacheOperationContext

相同点在于:都是为原有的对象做增强, 使其可以获取更多的信息, 且都使用了链表结构
Netty是通过prevnext指针维护了链表
而Spring直接使用了LinkedMultiValueMap

继续跟踪getOperationContext方法

ps: 这一块涉及到@Cachexxx注释上各种属性的解析,比较细枝末节,但这些被解析的属性待会又要用上

	protected CacheOperationContext getOperationContext(
			CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
		
		CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
		
		return new CacheOperationContext(metadata, args, target);
	}

CacheOperationContext 主要包含了以下属性

class CacheOperationContext {
	
	// 1. CacheOperationMetadata 
	private final CacheOperationMetadata metadata;
	
	// 2. 执行当前方法的参数
	private final Object[] args;

	// 3. 执行当前方法的对象
	private final Object target;

	// 4. 缓存集合
	private final Collection<? extends Cache> caches;
	
}

CacheOperationMetadata 主要包含了以下属性

class CacheOperationMetadata {
	
	// 1. CacheOperation 
	private final CacheOperation operation;
	
	// 2. 当前执行的方法
	private final Method method;
	
	// 3. 当前执行方法的对象类
	private final Class<?> targetClass;
	
	// 4. 缓存key的生成器
	private final KeyGenerator keyGenerator;
	
	// 5. 用于解析出具体的 { Cache } 对象
	private final CacheResolver cacheResolver;
}

继续跟踪getCacheOperationMetadata方法

看下CacheOperationMetadate是如何通过注解的属性解析成对象的

	protected CacheOperationMetadata getCacheOperationMetadata(
			CacheOperation operation, Method method, Class<?> targetClass) {

		KeyGenerator operationKeyGenerator;
		
		// 1. 如果注解声明了 "keyGenerator" 属性
		if (StringUtils.hasText(operation.getKeyGenerator())) {
			// 通过 { BeanName } 从容器中获取具体的 { KeyGenerator } 镀锡
			operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
		}
		else {
			// 否则使用默认的
			operationKeyGenerator = getKeyGenerator();
		}
		
		CacheResolver operationCacheResolver;
		
		// 2. 如果注解声明了 "cacheResolver" 属性
		if (StringUtils.hasText(operation.getCacheResolver())) {
			operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
		}
		// 2.1 else if 注解声明了 "CacheManager" 属性 
		else if (StringUtils.hasText(operation.getCacheManager())) {
			CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
			operationCacheResolver = new SimpleCacheResolver(cacheManager);
		}
		// 2.2 都没有声明, 那么使用默认的 { CacheResolver }
		else {
			operationCacheResolver = getCacheResolver();
			Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
		}
		
		// 3. 创建MetaData对象并且返回
		metadata = new CacheOperationMetadata(operation, method, targetClass,
				operationKeyGenerator, operationCacheResolver);
				
		return metadata;
	}

在这里插入图片描述

6.3 SpringBoot下的CacheManager

在使用Spring-Boot的情况下,我们只需要通过一个属性,就可以向Spring容器中注入默认的CacheManager

spring:
	cache:
		type: redis

例如这样声明, Spring就会向容器中注入RedisCacheManager

如此:即使我们没有在注解上声明CacheResovler和CacheManager也不会抛出异常

6.4 回到getOperationContext方法
	protected CacheOperationContext getOperationContext(
			CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
		
		// 1. 解析metadata
		CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
			
		// 2. 创建CacheOperationContext
		return new CacheOperationContext(metadata, args, target);
	}

6.5 回到execute方法

此时,CacheOperationContexts已经创建好了,接而又调用了重载的execute方法

execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));

这个execute方法是最核心的,包含了Spring-Cache的完整流程

	@Nullable
	private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) 
		
		// 1. 处理@CacheEvict, 在方法执行 [前] 使缓存过期, 具体看 <6.5.1>
		
		// 注意这个: contexts.get(CacheEvictOperation.class), 下面还会被多次调用
		
		// contexts是一个Map, key是 Class<extends CacheOperation>, value就是对应的CacheOperation
		
		processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
				CacheOperationExpressionEvaluator.NO_RESULT);

		// 2. 处理@Cacheable注解, 具体看 <6.5.2>
		Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

		List<CachePutRequest> cachePutRequests = new LinkedList<>();
		
		// 3. cacheHit == null的几种情况
		// - 方法没有使用@Cacheable注解
		// - 方法没有执行过
		// - 方法的缓存失效了
		if (cacheHit == null) {
		
			// 4. 那么收集方法上的@Cacheable注解, 将其转换为 { CachePutRequest }
			// 并且放入到 { cachePutRequests  } 集合中, 具体看 <6.5.3>
			collectPutRequests(contexts.get(CacheableOperation.class),
					CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
		}

		Object cacheValue;
		Object returnValue;

		// 4. 可以进到if, 那么表示,方法上使用了 `@Cacheable`注解, 并且已经缓存过结果了
		// 那么直接将缓存的结果返回
		if (cacheHit != null && !hasCachePut(contexts)) {
			// 得到缓存的value
			cacheValue = cacheHit.get();
			
			// 包装value, 用于返回
			// wrapCacheValue: 处理返回类型是 { java.util.Optional } 的情况
			returnValue = wrapCacheValue(method, cacheValue);
		}
		else {
			
			// 5. 如果没有缓存的结果可以返回, 那么执行方法!!!
			returnValue = invokeOperation(invoker);
			
			// unwrapReturnValue: 处理返回类型是 { java.util.Optional } 的情况
			cacheValue = unwrapReturnValue(returnValue);
		}

		// 6. 找到方法上的 { @CachePut } 注解对应的 { CachePutContext }
		// 将其转换为 { CachePutRequest }, 并且放入 { cachePutRequests }集合中
		// 和 <6.5.3> 调用的同一个方法
		collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

		// 7. 处理 { cachePutRequests } 集合, 将方法执行结果放入缓存中
		for (CachePutRequest cachePutRequest : cachePutRequests) {
			cachePutRequest.apply(cacheValue);
		}

		// 8. 处理@CacheEvict, 在方法执行 [后] 使缓存过期
		// 和 <6.5.1> 调用的同一个方法
		processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

		return returnValue;
	}
6.5.1 processCacheEvicts

当前调用发生在执行方法前

只有将@CacheEvictbeforeInvocation声明为true才会执行

	private void processCacheEvicts(
			Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {

		for (CacheOperationContext context : contexts) {
		
			CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
			
			if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
			
				// 使缓存失效
				performCacheEvict(context, operation, result);
			}
			
		}
	}
6.5.1 findCachedItem

@Cacheable注解的作用:
如果方法之前执行过,那么将方法的执行结果放入缓存,下次调用方法时直接返回缓存的内容

	private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
		Object result = CacheOperationExpressionEvaluator.NO_RESULT;
		
		// 遍历全部的@Cacheable注解
		for (CacheOperationContext context : contexts) {
			
			// 是否可以通过注解上的 { condition属性 }
			if (isConditionPassing(context, result)) {
				
				// 通过 { KeyGenerator } 生成缓存key
				Object key = generateKey(context, result);
				
				// 通过 缓存key 获取缓存的内容
				Cache.ValueWrapper cached = findInCaches(context, key);
				
				if (cached != null) {
					return cached;
				}
				
			}
			
		}
		return null;
	}
6.5.3 collectPutRequests

这个方法的作用是:将CacheableContext转换为CachePutRequest,并且放入到putRequests集合中

那为什么要转换一次呢?

原因很简单,因为被@Cacheable注解的方法,方法的返回结果需要被缓存起来
并且,被@CachePut注解的方法,方法的结果也需要被缓存起来

Spring为了保证接下来代码的一致性,在此处将CacheableOperation转换为了CachePutRequest

	private void collectPutRequests(Collection<CacheOperationContext> contexts,
			@Nullable Object result, Collection<CachePutRequest> putRequests) {

		for (CacheOperationContext context : contexts) {
			
			// 通过 { condition }
			if (isConditionPassing(context, result)) {
			
				Object key = generateKey(context, result);
		
				// 转换为 { CachePutRequest }
				// 放入 { putRequests } 集合中
				putRequests.add(new CachePutRequest(context, key));
			}
			
		}
	}

7. 总结

  1. 首先,通过EnableCaching注解引入CachingConfigurationSelector
  2. CachingConfigurationSelector注入ProxyCachingConfiguration到容器中
  3. ProxyCachingConfigurationBeanFactoryCacheOperationSourceAdvisor注入到容器中
  4. BeanFactoryCacheOperationSourceAdvisor基于Spring-AOP创建代理对象
  5. 代理对象执行方法时被CacheInterceptor拦截
  6. CacheInterceptor@Cacheable @CacheEvict @CachePut转换为对应的CacheOperationContext
  7. CacheInterceptorexecute()方法中基于CacheOperationContext介入方法的执行流程,处理各种缓存逻辑
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值