栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Spring Cache】六 CacheInterceptor 相关

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Spring Cache】六 CacheInterceptor 相关

【Spring Cache】六 CacheInterceptor 相关

前言CacheInterceptorCacheAspectSupport

1 - 关于缓存操作的属性相关2 - 从 execute 方法入手3 - CacheOperationContexts4 - getOperationContext5 - execute AbstractCacheInvoker总结

前言

之前的章节了解到,@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#executeCacheOperationInvoker 接口代表被缓存方法的执行,这里内部类的实现即 MethodInvocation#proceed关于 MethodInterceptor MethodInvocation 等更多细节,可以参考下文:

【源码】Spring AOP 2 Advice

【源码】Spring AOP 3 Joinpoint

CacheAspectSupport

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

1 - 关于缓存操作的属性相关
	
	private final Map metadataCache = new ConcurrentHashMap<>(1024);

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

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

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

	@Nullable
	private SingletonSupplier cacheResolver;

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

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

	// 这里通常是基于 CacheConfigurer 进行配置
	public void configure(
			@Nullable Supplier errorHandler, @Nullable Supplier keyGenerator,
			@Nullable Supplier cacheResolver, @Nullable Supplier 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 {
				
				setCacheManager(this.beanFactory.getBean(CacheManager.class));
			}
			// ...
		}
		this.initialized = true;
	}

首先这里是一些缓存操作属性的定义,可以理解为默认的对象属性指定了 KeyGenerator 的默认实例是 SimpleKeyGenerator,之前的章节已经了解过之前了解 ProxyCachingConfiguration 配置类时了解过缓存属性可以基于 CachingConfigurer 类进行自定义配置,这里就是由 configure 方法处理:

CacheErrorHandler 未提供自定义配置时默认实例为 SimpleCacheErrorHandlerKeyGenerator 未提供自定义配置时默认实例为 SimpleKeyGeneratorcacheResolver 未提供自定义配置时默认实例为基于自定义 cacheManager 的 SimpleCacheResolver,当然这里如果也没有提供 cacheManager 那就是 null 了,具体细节可见 SupplierUtils SimpleCacheResolver#of 的处理 本类还是一个 SmartInitializingSingleton,因此容器管理下会执行 afterSingletonsInstantiated,此处支持在 cacheResolver 为 null 时尝试从容器中获取 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 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();
	}

CacheInterceptor 的 invoke 方法是委托到这里的,即整个缓存切面的执行逻辑在这里实现先基于 CacheOperationSource(这里就是 AnnotationCacheOperationSource)获取目标方法上的所有 CacheOperationSources然后基于这些 CacheOperationSources 创建对应的 CacheOperationContexts 进而继续往下执行 3 - CacheOperationContexts

	
	private class CacheOperationContexts {

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

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

		public CacheOperationContexts(Collection 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 cacheOperationContexts = this.contexts.get(CacheableOperation.class);
			if (cacheOperationContexts == null) {
				return false;
			}

			
			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) {

		
		CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);

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

			
			if (StringUtils.hasText(operation.getKeyGenerator())) {
				operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
			}
			else {
				operationKeyGenerator = getKeyGenerator();
			}

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

		
		List cachePutRequests = new ArrayList<>();

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

		
		Object cachevalue;
		Object returnValue;

		
		if (cacheHit != null && !hasCachePut(contexts)) {
			// If there are no put requests, just use the cache hit
			cachevalue = cacheHit.get();
			returnValue = wrapCachevalue(method, cachevalue);
		}
		
		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 的实现逻辑了:

    首先如果之前构造 CacheOperationContexts 推断目标缓存方法需要同步执行时,会最终依赖 Cache#get(Object key, Callable valueLoader) 方法执行 CacheableOperation 操作,它需要实现类保证线程安全,比如 ConcurrentMapCache 就是基于 ConcurrentMap#computeIfAbsent 方法来保证同步性的,第一章节有了解过如果目标缓存方法不需要同步执行,则最先执行属性 beforeInvocation = true 的 CacheEvictOperation 操作,即在方法执行前剔除缓存(基于 allEntries 属性决定是清除所有缓存还是指定 key 的缓存)接下来会尝试从缓存中获取对应的值(以指定的缓存集合中获取的第一个为准),如果缓存未命中,则对应的 CacheableOperation 就被视为 CachePutOperation 了(不难理解)定义 cacheval 为更新缓存的值,定义 retuanVal 为返回给调用方的值:

    缓存命中时:cacheval 即命中的值,retuanVal 有必要的话基于 cacheval 包装成 Optional缓存未命中时:retuanVal 即目标方法的执行结果,cacheval 有必要的话从 Optional 包装取出 收集所有的 CachePutRequest,这里包括了两种:

      未命中缓存的 CacheableOperationCachePutOperation
    执行这些 CachePutRequest,本质上就是 Cache#put最后执行的是属性 beforeInvocation = false 的 CacheEvictOperation 操作,即在方法执行后剔除缓存(基于 allEntries 属性决定是清除所有缓存还是指定 key 的缓存),当然这里就可以支持 unless 属性判断了(因为有方法的返回值)至此就是整个 Spring Cache 的实现逻辑了
AbstractCacheInvoker
public abstract class AbstractCacheInvoker {

	protected SingletonSupplier 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 流程总结

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/727278.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号