springCache源码解读

基础介绍

注解介绍

  • @EnableCaching 表示开启springCache
  • @CacheConfig 设置缓存配置
  • @CacheEvict 清除缓存
  • @CachePut 结果缓存
  • @Cacheable 方法结果缓存

基本使用

基本使用

源码流程解读

启动配置装配

使用springCache的前提是,配置类上有@EnableCaching注解。可以看该注解的定义,里面有一个@Import(CachingConfigurationSelector.class),这个import会在程序启动的时候扫描到,进而加载类CachingConfigurationSelector

// CachingConfigurationSelector#selectImports   5.3.12  70行
@Override
public String[] selectImports(AdviceMode adviceMode) {
	switch (adviceMode) {
		case PROXY:
			return getProxyImports();
		case ASPECTJ:
			return getAspectJImports();
		default:
			return null;
	}
}

这里会根据不同的代理模式选择不同的import,我这里的PROXY,因此会装载AutoProxyRegistrarProxyCachingConfiguration 这两个类,其中后者会生成一个CacheInterceptor实例,该实例会对后续的请求进行拦截,然后进行缓存操作。

请求触发

触发点

被其他方法调用会触发方法拦截,bug入口可以从CacheInterceptor#invoke开始看。

CacheAspectSupport#execute

protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
	if (this.initialized) {
		Class<?> targetClass = getTargetClass(target);
		// 获取 cache operations 它们会在启动的时候装配
		CacheOperationSource cacheOperationSource = getCacheOperationSource();
		if (cacheOperationSource != null) {
		// 获取对应的 cache operations 这里大部分能直接拿到,小部分需要计算获取  TODO 什么场景呢
			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();
}
获取上下文
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
		Object[] args, Object target, Class<?> targetClass) {

	this.contexts = new LinkedMultiValueMap<>(operations.size());
	for (CacheOperation op : operations) {
		this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
	}
	//  @Cacheable  同步标记
	this.sync = determineSyncFlag(method);
}


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);
}
构造元数据
protected CacheOperationMetadata getCacheOperationMetadata(
			CacheOperation operation, Method method, Class<?> targetClass) {
	// 构造缓存key,先查询=缓存
	CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);
	CacheOperationMetadata metadata = this.metadataCache.get(cacheKey);
	// 没有则进行构造
	if (metadata == null) {
		// 判断定义的时候有没有  keyGenerator 有则加载这个类,没有则用通用的
		KeyGenerator operationKeyGenerator;
		if (StringUtils.hasText(operation.getKeyGenerator())) {
			operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
		}
		else {
			operationKeyGenerator = getKeyGenerator();
		}
		// 判断定义的时候有没有 cacheResolver或cacheManager,有则加载定义的类,没有则使用通用的
		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();
			Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
		}
		// 构造元数据,加入缓存
		metadata = new CacheOperationMetadata(operation, method, targetClass,
				operationKeyGenerator, operationCacheResolver);
		this.metadataCache.put(cacheKey, metadata);
	}
	return metadata;
}
构造上下文
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
	this.metadata = metadata;
	// 可变参数处理
	this.args = extractArgs(metadata.method, args);
	this.target = target;
	// 根据名字获取对应的cache
	this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
	// caches 名字集合
	this.cacheNames = createCacheNames(this.caches);
}

// 变长入参处理
private Object[] extractArgs(Method method, Object[] args) {
	if (!method.isVarArgs()) {
		return args;
	}
	// 如果是可变入参,最后一个参数是数组
	Object[] varArgs = ObjectUtils.toObjectArray(args[args.length - 1]);
	Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
	// 复制数据
	System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
	System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
	return combinedArgs;
}


execute 核心步骤
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
	// 处理@Cacbeable 同步操作
	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 {
				// 查询缓存,缓存命中则包装结果返回,如果没有命中则同步赋值(synchronized)
				return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
			}
			catch (Cache.ValueRetrievalException ex) {
				// Directly propagate ThrowableWrapper from the invoker,
				// or potentially also an IllegalArgumentException etc.
				ReflectionUtils.rethrowRuntimeException(ex.getCause());
			}
		}
		// condition没有通过,直接调用方法   注意:这里可以看出设置了sync=true的@Cacheable是独占的,
		// 如果在方法层面顶一个多个 @Cache 注解,只有它会生效。
		else {
			return invokeOperation(invoker);
		}
	}
	// 处理@CacheEvict 前置执行操作
	processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
			CacheOperationExpressionEvaluator.NO_RESULT);

	// 判断是否命中缓存
	Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

	// 收集CachePut操作
	List<CachePutRequest> cachePutRequests = new ArrayList<>();
	// 如果缓存没有名中国,则将@Cacheable 收集到  cachePutRequests 
	if (cacheHit == null) {
		collectPutRequests(contexts.get(CacheableOperation.class),
				CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
	}

	Object cacheValue;
	Object returnValue;
	// 如果缓存命中且没有  CachePut操作。则直接使用缓存
	if (cacheHit != null && !hasCachePut(contexts)) {
		// If there are no put requests, just use the cache hit
		cacheValue = cacheHit.get();
		returnValue = wrapCacheValue(method, cacheValue);
	}
	// 否则,调用对应方法
	else {
		// Invoke the method if we don't have a cache hit
		returnValue = invokeOperation(invoker);
		cacheValue = unwrapReturnValue(returnValue);
	}

	//将 @CachePuts 收集到 cachePutRequests
	collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

	// 对cachePutRequest执行更新缓存操作(@CachePut和@Cacheable)
	for (CachePutRequest cachePutRequest : cachePutRequests) {
		cachePutRequest.apply(cacheValue);
	}

	// 执行  @CacheEvict 后置执行操作
	processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);

	return returnValue;
}
processCacheEvicts
private void processCacheEvicts(
			Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
	// 遍历,校验是否是前置执行,并且符合 condition条件
	for (CacheOperationContext context : contexts) {
		CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
		if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
			performCacheEvict(context, operation, result);
		}
	}
}

private void performCacheEvict(
			CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
	Object key = null;
	for (Cache cache : context.getCaches()) {
		// 判断是否是全局缓存(对整个Cache生效)
		if (operation.isCacheWide()) {
			// 打印日志,移除清空Cache
			logInvalidating(context, operation, null);
			// 这里有是否立即处理判断,进去看了一下,RedisCache没啥区别。
			doClear(cache, operation.isBeforeInvocation());
		}
		else {
			// 构造对应的key,然后把这个key的缓存移除
			if (key == null) {
				key = generateKey(context, result);
			}
			logInvalidating(context, operation, key);
			doEvict(cache, key, operation.isBeforeInvocation());
		}
	}
}
findCachedItem
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
	Object result = CacheOperationExpressionEvaluator.NO_RESULT;
	for (CacheOperationContext context : contexts) {
	// condition 条件是否通过,通过的话构造缓存key 查询缓存
		if (isConditionPassing(context, result)) {
			Object key = generateKey(context, result);
			Cache.ValueWrapper cached = findInCaches(context, key);
			if (cached != null) {
				return cached;
			}
			else {
				if (logger.isTraceEnabled()) {
					logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
				}
			}
		}
	}
	return null;
}
collectPutRequests
private void collectPutRequests(Collection<CacheOperationContext> contexts,
			@Nullable Object result, Collection<CachePutRequest> putRequests) {
	// 遍历,构造key 构造CachePutRequest
	for (CacheOperationContext context : contexts) {
		if (isConditionPassing(context, result)) {
			Object key = generateKey(context, result);
			putRequests.add(new CachePutRequest(context, key));
		}
	}
}
CachePutRequest#apply
public void apply(@Nullable Object result) {
	// 对结果进行 unless 校验
	if (this.context.canPutToCache(result)) {
		// 置入缓存
		for (Cache cache : this.context.getCaches()) {
			doPut(cache, this.key, result);
		}
	}
}

// RedisCache#put()
public void put(Object key, @Nullable Object value) {
	// 空值校验
	Object cacheValue = preProcessCacheValue(value);
	if (!isAllowNullValues() && cacheValue == null) {
		throw new IllegalArgumentException(String.format(
				"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
				name));
	}
	// 写入redis   
	cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}

缓存写入

构造缓存key
// RedisCache#createAndConvertCacheKey
private byte[] createAndConvertCacheKey(Object key) {
	return serializeCacheKey(createCacheKey(key));
}

protected String createCacheKey(Object key) {
	// 转换key 
	String convertedKey = convertKey(key);
	// 如果不使用前缀,则直接返回key
	if (!cacheConfig.usePrefix()) {
		return convertedKey;
	}
	// 构造key  最终key格式  CacheName::key  例如  c10m::name
	return prefixCacheKey(convertedKey);
}

private String prefixCacheKey(String key) {
	return cacheConfig.getKeyPrefixFor(name) + key;
}

// CacheKeyPrefix   最终就是 value::key
static CacheKeyPrefix simple() {
	return name -> name + SEPARATOR;
}
写入缓存
//  DefaultRedisCacheWriter#put()
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
	Assert.notNull(name, "Name must not be null!");
	Assert.notNull(key, "Key must not be null!");
	Assert.notNull(value, "Value must not be null!");
	// 写入redis
	execute(name, connection -> {
		if (shouldExpireWithin(ttl)) {
			connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());
		} else {
			connection.set(key, value);
		}
		return "OK";
	});
	// redis 实际没啥用
	statistics.incPuts(name);
}

private <T> T execute(String name, Function<RedisConnection, T> callback) {
	// 可以看到这里使用了  connectionFactory,如果使用了 Jedis,则会从连接池中取连接
	RedisConnection connection = connectionFactory.getConnection();
	try {
		checkAndPotentiallyWaitUntilUnlocked(name, connection);
		return callback.apply(connection);
	} finally {
		connection.close();
	}
}

构造缓存key

这里用来处理 @Cache 系列中的key属性 里面有sPEL解析,感兴趣的可以看看。 condition的判断也是类似的。

// CacheAspectSupport#generateKey
protected Object generateKey(@Nullable Object result) {
	if (StringUtils.hasText(this.metadata.operation.getKey())) {
		EvaluationContext evaluationContext = createEvaluationContext(result);
		return evaluator.key(this.metadata.operation.getKey(), this.metadata.methodKey, evaluationContext);
	}
	return this.metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
}

拦截器怎么起作用的

//  ProxyCachingConfiguration#cacheAdvisor()
// 注意:这里没用默认的名字,用了 org.springframework.cache.config.internalCacheAdvisor
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
		CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {

	BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
	advisor.setCacheOperationSource(cacheOperationSource);
	advisor.setAdvice(cacheInterceptor);
	if (this.enableCaching != null) {
		advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
	}
	return advisor;
}

//方法调用触发的时候,会创建代理  AbstractAutoProxyCreator#wrapIfNecessary

总结

  • CacheOperation 是方法层面的,也就是说一个方法可以同时被@Cacheable@CachePut@CacheEvict同时注释
  • Cacheable如果设置属性synctrue,则会使unless失效。如果方法层面有多个@Cache注解,则其他的都失效
  • 如果集成了redis,不用害怕使用@Cache系列不会释放redis连接,它最终使用的是jedis连接池。
  • redis最终的缓存key拼凑,CacheName::key,示例:CacheName = c10mkey = name ,则最终redis中的缓存keyc10m::name
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值