赞
踩
@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
,因此会装载AutoProxyRegistrar
和ProxyCachingConfiguration
这两个类,其中后者会生成一个CacheInterceptor
实例,该实例会对后续的请求进行拦截,然后进行缓存操作。
被其他方法调用会触发方法拦截,bug入口可以从
CacheInterceptor#invoke
开始看。
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; }
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; }
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()); } } }
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; }
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));
}
}
}
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()); }
// 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(); } }
这里用来处理 @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
如果设置属性sync
为true
,则会使unless
失效。如果方法层面有多个@Cache
注解,则其他的都失效redis
,不用害怕使用@Cache
系列不会释放redis
连接,它最终使用的是jedis
连接池。redis
最终的缓存key
拼凑,CacheName::key
,示例:CacheName = c10m
、key = name
,则最终redis
中的缓存key
为c10m::name
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。