【Spring Cache】六 CacheInterceptor 相关

本文详细解读了SpringCache中的CacheInterceptor,从属性配置、execute方法入手,介绍了CacheOperationContexts和关键方法如getOperationContext。深入理解了缓存操作的执行流程和策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

之前的章节了解到,@EnableCaching 最终会引入组件 BeanFactoryCacheOperationSourceAdvisor,基于 Advisor = Advice + Pointcut,其中 Advice 就是 CacheInterceptor,本章节具体了解 CacheInterceptor 相关类:即 Spring Cache 行为的实现逻辑

CacheInterceptor

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {

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

		Object target = invocation.getThis();
		Assert.state(target != null, "Target must not be null");
		try {
			return execute(aopAllianceInvoker, target, method, invocation.getArguments());
		}
		catch (CacheOperationInvoker.ThrowableWrapper th) {
			throw th.getOriginal();
		}
	}

}
  • 首先,CacheInterceptor 是一个 MethodInterceptor,它代表整个切面拦截链的执行,执行逻辑封装在父类方法 CacheAspectSupport#execute
  • CacheOperationInvoker 接口代表被缓存方法的执行,这里内部类的实现即 MethodInvocation#proceed
  • 关于 MethodInterceptor MethodInvocation 等更多细节,可以参考下文:

【源码】Spring AOP 2 Advice

【源码】Spring AOP 3 Joinpoint

CacheAspectSupport

这是整个 CacheInterceptor 逻辑实现的核心基类,所以内容比较多,以个人角度分几个部分了解:

1 - 关于缓存操作的属性相关

	/**
	 * k:CacheOperationCacheKey
	 * v:CacheOperationMetadata 缓存操作元数据
	 */
	private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = new ConcurrentHashMap<>(1024);

	// 负责 SpEL 解析
	private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();

	// 用来解析指定方法的 CacheOperation
	@Nullable
	private CacheOperationSource cacheOperationSource;

	// 默认的 KeyGenerator 是 SimpleKeyGenerator
	private SingletonSupplier<KeyGenerator> keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);

	@Nullable
	private SingletonSupplier<CacheResolver> cacheResolver;

	// 本身是一个 BeanFactoryAware
	@Nullable
	private BeanFactory beanFactory;

	// 属性初始化的标识
	private boolean initialized = false;

	// 这里通常是基于 CacheConfigurer 进行配置
	public void configure(
			@Nullable Supplier<CacheErrorHandler> errorHandler, @Nullable Supplier<KeyGenerator> keyGenerator,
			@Nullable Supplier<CacheResolver> cacheResolver, @Nullable Supplier<CacheManager> cacheManager) {

		// 默认 SimpleCacheErrorHandler
		this.errorHandler = new SingletonSupplier<>(errorHandler, SimpleCacheErrorHandler::new);

		// 默认 SimpleKeyGenerator
		this.keyGenerator = new SingletonSupplier<>(keyGenerator, SimpleKeyGenerator::new);

		// 默认基于 cacheManager 构造 SimpleCacheResolver
		// 至此,cacheResolver 有可能还是 null
		this.cacheResolver = new SingletonSupplier<>(cacheResolver,
				() -> SimpleCacheResolver.of(SupplierUtils.resolve(cacheManager)));
	}

	@Override
	public void afterSingletonsInstantiated() {
		if (getCacheResolver() == null) {
			try {
				/**
				 * 此时如果 cacheResolver 为 null 就尝试从容器中获取 CacheManager
				 * 		来构造 SimpleCacheResolver
				 * 此时如果容器中没有 CacheManager 则抛出异常
				 */
				setCacheManager(this.beanFactory.getBean(CacheManager.class));
			}
			// ...
		}
		this.initialized = true;
	}
  • 首先这里是一些缓存操作属性的定义,可以理解为默认的对象属性
  • 指定了 KeyGenerator 的默认实例是 SimpleKeyGenerator,之前的章节已经了解过
  • 之前了解 ProxyCachingConfiguration 配置类时了解过缓存属性可以基于 CachingConfigurer 类进行自定义配置,这里就是由 configure 方法处理:
    • CacheErrorHandler 未提供自定义配置时默认实例为 SimpleCacheErrorHandler
    • KeyGenerator 未提供自定义配置时默认实例为 SimpleKeyGenerator
    • cacheResolver 未提供自定义配置时默认实例为基于自定义 cacheManagerSimpleCacheResolver,当然这里如果也没有提供 cacheManager 那就是 null 了,具体细节可见 SupplierUtils SimpleCacheResolver#of 的处理
  • 本类还是一个 SmartInitializingSingleton,因此容器管理下会执行 afterSingletonsInstantiated,此处支持在 cacheResolvernull 时尝试从容器中获取 cacheManager 并基于此创建 SimpleCacheResolver,如果此处依然无法创建 cacheResolver 则抛出异常,最后标识 initialized = true

2 - 从 execute 方法入手

	@Nullable
	protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
		if (this.initialized) {
			Class<?> targetClass = getTargetClass(target);

			// 获取 CacheOperationSource,此处就是 AnnotationCacheOperationSource 了
			CacheOperationSource cacheOperationSource = getCacheOperationSource();
			if (cacheOperationSource != null) {
				
				// 或许对应方法的所有 CacheOperation
				Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
				if (!CollectionUtils.isEmpty(operations)) {
					
					// 基于 CacheOperation 集合创建的 CacheOperationContexts 执行 execute 方法
					return execute(invoker, method,
							new CacheOperationContexts(operations, method, args, target, targetClass));
				}
			}
		}

		// 如果不在容器下管理此单例的话,这里就不执行缓存操作了
		return invoker.invoke();
	}
  • CacheInterceptorinvoke 方法是委托到这里的,即整个缓存切面的执行逻辑在这里实现
  • 先基于 CacheOperationSource(这里就是 AnnotationCacheOperationSource)获取目标方法上的所有 CacheOperationSources
  • 然后基于这些 CacheOperationSources 创建对应的 CacheOperationContexts 进而继续往下执行

3 - CacheOperationContexts

	/**
	 * CacheOperationContexts 维护这样一组结构:
	 * 		一个方法对应多种操作
	 * 		每种操作对应多个操作上下文 CacheOperationContext
	 * 比如:
	 * @Cacheable("cache1")
	 * @Cacheable("cache2")
	 * @CachePut("cache3")
	 * public void test() {}
	 *
	 * 则上述方法 test 对应的 CacheOperationContexts 维护结构大致为:
	 * Cacheable.class -> [CacheOperationContext1, CacheOperationContext2]
	 * CachePut.class -> [CacheOperationContext3]
	 */
	private class CacheOperationContexts {

		// MultiValueMap: Spring 提供的一个可以重复同一个 key 的 Map
		private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts;

		// 当前缓存操作是否需要同步执行
		private final boolean sync;

		public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
				Object[] args, Object target, Class<?> targetClass) {

			// 基于传入的 CacheOperation 集合构造对应的 CacheOperationContexts
			this.contexts = new LinkedMultiValueMap<>(operations.size());
			for (CacheOperation op : operations) {
				this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
			}

			// 推断当前缓存操作是否需要同步执行
			this.sync = determineSyncFlag(method);
		}

		// ...

		// 推断是否同步执行
		private boolean determineSyncFlag(Method method) {

			// 同步操作只支持 CacheableOperation,如果没有那就是不需要同步
			List<CacheOperationContext> cacheOperationContexts = this.contexts.get(CacheableOperation.class);
			if (cacheOperationContexts == null) {
				return false;
			}

			/**
			 * 如果上述 CacheableOperation 操作里只要有一个指定属性 sync = true 的,
			 * 			就开始判断是否真的满足同步执行条件
			 */
			boolean syncEnabled = false;
			for (CacheOperationContext cacheOperationContext : cacheOperationContexts) {
				if (((CacheableOperation) cacheOperationContext.getOperation()).isSync()) {
					syncEnabled = true;
					break;
				}
			}
			if (syncEnabled) {
				// 多个 contexts 意味多种缓存操作,不支持同步执行
				if (this.contexts.size() > 1) {
					throw new IllegalStateException(
							"...");
				}
				// 也不支持多个 @Cacheable(sync=true) 并行
				if (cacheOperationContexts.size() > 1) {
					throw new IllegalStateException(
							"...");
				}

				// @Cacheable(sync=true) 操作也不支持同时指定多个 cacheName
				CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next();
				CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation();
				if (cacheOperationContext.getCaches().size() > 1) {
					throw new IllegalStateException(
							"...");
				}

				// 缓存操作不支持指定 unless 属性
				if (StringUtils.hasText(operation.getUnless())) {
					throw new IllegalStateException(
							"...");
				}
				return true;
			}
			return false;
		}
	}
  • 之前的章节了解过缓存操作执行上下文相关的 CacheOperationInvocationContext 接口及其内部类实现 CacheOperationContext,这里的 CacheOperationContexts 是对 CacheOperationContext 的一层封装
  • CacheOperationContexts 为目标方法上每种操作维护对应的一组 CacheOperationContexts,毕竟同一个缓存操作注解可以重复添加(但这样不支持 sync 同步属性)
  • 提供 determineSyncFlag 方法用来推断当前缓存操作是否需要同步执行,这里印证了之前对 sync 属性的定义:
    • 它只适用于 CacheableOperation 即对应 @Cacheable 注解
    • 不支持与其他缓存操作混用
    • 不支持 @Cacheable 的合并
    • 对唯一的 @Cacheable 也不支持指定多个 cacheName
    • 同步操作时不支持 unless 属性

4 - getOperationContext

	protected CacheOperationContext getOperationContext(
			CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {

		// 构造目标方法目标操作上的 CacheOperationMetadata
		CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
		return new CacheOperationContext(metadata, args, target);
	}

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

		/**
		 * 这个 key 是由 CacheOperation 和被注解元素的 AnnotatedElementKey 组成的
		 * 		针对同一方法上的同一种元素,也会给予其不同的属性创建对应的缓存,具体可以
		 * 		看下 CacheOperation#equals 方法
		 */
		CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);

		// 先从缓存中获取,获取不到就创建
		CacheOperationMetadata metadata = this.metadataCache.get(cacheKey);
		if (metadata == null) {
			KeyGenerator operationKeyGenerator;

			/**
			 * 如果注解上指定了 keyGenerator,则从容器中获取,否则就是 CacheConfigurer
			 * 		指定或默认的 SimpleKeyGenerator
			 */
			if (StringUtils.hasText(operation.getKeyGenerator())) {
				operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
			}
			else {
				operationKeyGenerator = getKeyGenerator();
			}

			/**
			 * 如果注解指定了 cacheResolver 就从容器获取,又或者注解指定了 cacheManager
			 * 		则从容器中获取并依此创建 SimpleCacheResolver
			 */
			CacheResolver operationCacheResolver;
			if (StringUtils.hasText(operation.getCacheResolver())) {
				operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
			}
			else if (StringUtils.hasText(operation.getCacheManager())) {
				CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
				operationCacheResolver = new SimpleCacheResolver(cacheManager);
			}
			/**
			 * 如果注解上没有指定,CacheConfigurer 也没有指定,则此处就获取不到
			 * 		cacheResolver 抛出异常
			 */
			else {
				operationCacheResolver = getCacheResolver();
			}
			
			// 基于这些属性创建 CacheOperationMetadata
			metadata = new CacheOperationMetadata(operation, method, targetClass,
					operationKeyGenerator, operationCacheResolver);
			this.metadataCache.put(cacheKey, metadata);
		}
		return metadata;
	}
  • 这里是构造 CacheOperation 对应的 CacheOperationContext,核心是构造 CacheOperationMetadata,即缓存操作相关的元数据
  • 其中创建 CacheOperationMetadata 时:
    • 会优先使用注解上指定的 keyGenerator:从容器中获取对应 bean 实例
    • 同样地也会优先使用基于注解指定的 cacheResolver cacheManager 来构造对应的 cacheResolver
    • 至此我们大致可以排列配置的优先级:
      • 注解上指定的配置优先级最高
      • 其次是 CacheConfigurer 上指定的配置
      • 最后是缺省配置,比如 SimpleKeyGenerator SimpleCacheErrorHandler

5 - execute

	@Nullable
	private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {

		// 如果当前方法要进行同步缓存操作
		if (contexts.isSynchronized()) {
			CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
			if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
				Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
				Cache cache = context.getCaches().iterator().next();
				try {
					/**
					 * 返回值是 Optional 则包装缓存或执行结果后返回
					 * handleSynchronizedGet 方法依赖于缓存的 get(Object key, Callable<T> valueLoader)
					 * 		方法,即 get 不到就将 valueLoader::call 缓存进去并返回
					 * get 方法的实现保证同步性,比如 ConcurrentMapCache 基于 ConcurrentMap#computeIfAbsent
					 * 		方法保证同步
					 */
					return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
				}
				catch (Cache.ValueRetrievalException ex) {
					// ...
				}
			}
			else {
				// condition 不通过就直接执行目标方法了
				return invokeOperation(invoker);
			}
		}

		// 如果非同步操作
		// 先执行属性 beforeInvocation = true 的 CacheEvictOperation
		processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
				CacheOperationExpressionEvaluator.NO_RESULT);

		// 获取可以找到的第一个缓存值
		Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

		/**
		 * 这个集合会收集所有的 CachePut 操作,包括两种:
		 * 1)缓存未命中的 CacheableOperation
		 * 2)CachePutOperation
		 */
		List<CachePutRequest> cachePutRequests = new ArrayList<>();

		// 如果没有命中缓存,则此时 CacheableOperation 就相当于 CachePutOperation 了
		if (cacheHit == null) {
			collectPutRequests(contexts.get(CacheableOperation.class),
					CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
		}

		/**
		 * cacheValue:缓存值,但方法的执行结果是 Optional 时,
		 * 		cacheValue 表示真实值即 Optional#get
		 * returnValue:方法的执行结果,有可能被 Optional 包装
		 */
		Object cacheValue;
		Object returnValue;

		/**
		 * 如果缓存命中
		 * cacheVal 就是缓存值
		 * returnVal 有必要的话包装成 Optional
		 */
		if (cacheHit != null && !hasCachePut(contexts)) {
			// If there are no put requests, just use the cache hit
			cacheValue = cacheHit.get();
			returnValue = wrapCacheValue(method, cacheValue);
		}
		/**
		 * 如果缓存未命中
		 * retuanVal 就是目标方法的执行结果
		 * cacheVal 有必要的话取 Optional 的真实值
		 */
		else {
			returnValue = invokeOperation(invoker);
			cacheValue = unwrapReturnValue(returnValue);
		}

		// 把所有的 CachePutOperation 合并起来(@Cacheable 和 @CachePut)
		collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

		// 使用最终的缓存值 cacheValue 执行 doPut 操作
		for (CachePutRequest cachePutRequest : cachePutRequests) {
			cachePutRequest.apply(cacheValue);
		}

		// 最后执行 beforeInvocation = false 的剔除缓存操作
		processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

		// 方法的真实返回值
		return returnValue;
	}

这个方法就是 Spring Cache 的实现逻辑了:

  1. 首先如果之前构造 CacheOperationContexts 推断目标缓存方法需要同步执行时,会最终依赖 Cache#get(Object key, Callable<T> valueLoader) 方法执行 CacheableOperation 操作,它需要实现类保证线程安全,比如 ConcurrentMapCache 就是基于 ConcurrentMap#computeIfAbsent 方法来保证同步性的,第一章节有了解过
  2. 如果目标缓存方法不需要同步执行,则最先执行属性 beforeInvocation = trueCacheEvictOperation 操作,即在方法执行前剔除缓存(基于 allEntries 属性决定是清除所有缓存还是指定 key 的缓存)
  3. 接下来会尝试从缓存中获取对应的值(以指定的缓存集合中获取的第一个为准),如果缓存未命中,则对应的 CacheableOperation 就被视为 CachePutOperation 了(不难理解)
  4. 定义 cacheVal 为更新缓存的值,定义 retuanVal 为返回给调用方的值:
    • 缓存命中时:cacheVal 即命中的值,retuanVal 有必要的话基于 cacheVal 包装成 Optional
    • 缓存未命中时:retuanVal 即目标方法的执行结果,cacheVal 有必要的话从 Optional 包装取出
  5. 收集所有的 CachePutRequest,这里包括了两种:
    1. 未命中缓存的 CacheableOperation
    2. CachePutOperation
  6. 执行这些 CachePutRequest,本质上就是 Cache#put
  7. 最后执行的是属性 beforeInvocation = falseCacheEvictOperation 操作,即在方法执行后剔除缓存(基于 allEntries 属性决定是清除所有缓存还是指定 key 的缓存),当然这里就可以支持 unless 属性判断了(因为有方法的返回值)
  8. 至此就是整个 Spring Cache 的实现逻辑了

AbstractCacheInvoker

public abstract class AbstractCacheInvoker {

	protected SingletonSupplier<CacheErrorHandler> errorHandler;

	// 默认 SimpleCacheErrorHandler
	protected AbstractCacheInvoker() {
		this.errorHandler = SingletonSupplier.of(SimpleCacheErrorHandler::new);
	}

	// ...

	@Nullable
	protected Cache.ValueWrapper doGet(Cache cache, Object key) {
		try {
			return cache.get(key);
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCacheGetError(ex, cache, key);
			return null;
		}
	}

	protected void doPut(Cache cache, Object key, @Nullable Object result) {
		try {
			cache.put(key, result);
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCachePutError(ex, cache, key, result);
		}
	}

	protected void doEvict(Cache cache, Object key, boolean immediate) {
		try {
			if (immediate) {
				// 立即剔除
				cache.evictIfPresent(key);
			}
			else {
				// 不保证实时性
				cache.evict(key);
			}
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCacheEvictError(ex, cache, key);
		}
	}

	protected void doClear(Cache cache, boolean immediate) {
		try {
			if (immediate) {
				// 立即清除缓存
				cache.invalidate();
			}
			else {
				// 不保证实时性
				cache.clear();
			}
		}
		catch (RuntimeException ex) {
			getErrorHandler().handleCacheClearError(ex, cache);
		}
	}

}

顶层基类 AbstractCacheInvoker

  • 封装整个 Spring Cache 对底层缓存操作的基础方法 doGet doPut doEvict doClear
  • 操作过程中的异常由 CacheErrorHandler 接口处理,默认是 Spring 提供的唯一实现 SimpleCacheErrorHandler:直接抛出对应异常

总结

这段太干了,但是读都读了就尽可能全都贴上来,抛开实现的细节逻辑,我认为至少有以下几点直观收益:

  • 清楚的理解了缓存操作属性的配置方式和优先级原理:注解上指定的属性 > CacheConfigurer 指定的属性 > 缺省属性,因此可以更加合理的编写配置类
  • CacheOperationContexts 类的设计很有意思,优雅的管理了 method -> CacheOperation -> CacheOperationContext 的关系
  • CacheInterceptor CacheAspectSupport AbstractCacheInvoker 类层级的设计:
    • CacheInterceptor:站在 AOP 角度专注于 Cache Interceptor 的实现
    • CacheAspectSupport:负责实现核心的 Spring Cache 逻辑
    • AbstractCacheInvoker:提供对底层缓存的操作方法
  • 等等其他,其实主要用心看,值得学习的地方是很多的

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

下一篇:【Spring Cache】七 Spring Cache 流程总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值