【Spring Cache】六 CacheInterceptor 相关
前言
之前的章节了解到,@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- 关于
MethodInterceptorMethodInvocation等更多细节,可以参考下文:
【源码】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未提供自定义配置时默认实例为SimpleCacheErrorHandlerKeyGenerator未提供自定义配置时默认实例为SimpleKeyGeneratorcacheResolver未提供自定义配置时默认实例为基于自定义cacheManager的SimpleCacheResolver,当然这里如果也没有提供cacheManager那就是null了,具体细节可见SupplierUtilsSimpleCacheResolver#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<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();
}
CacheInterceptor的invoke方法是委托到这里的,即整个缓存切面的执行逻辑在这里实现- 先基于
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实例 - 同样地也会优先使用基于注解指定的
cacheResolvercacheManager来构造对应的cacheResolver - 至此我们大致可以排列配置的优先级:
- 注解上指定的配置优先级最高
- 其次是
CacheConfigurer上指定的配置 - 最后是缺省配置,比如
SimpleKeyGeneratorSimpleCacheErrorHandler
- 会优先使用注解上指定的
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 的实现逻辑了:
- 首先如果之前构造
CacheOperationContexts推断目标缓存方法需要同步执行时,会最终依赖Cache#get(Object key, Callable<T> valueLoader)方法执行CacheableOperation操作,它需要实现类保证线程安全,比如ConcurrentMapCache就是基于ConcurrentMap#computeIfAbsent方法来保证同步性的,第一章节有了解过 - 如果目标缓存方法不需要同步执行,则最先执行属性
beforeInvocation = true的CacheEvictOperation操作,即在方法执行前剔除缓存(基于allEntries属性决定是清除所有缓存还是指定key的缓存) - 接下来会尝试从缓存中获取对应的值(以指定的缓存集合中获取的第一个为准),如果缓存未命中,则对应的
CacheableOperation就被视为CachePutOperation了(不难理解) - 定义
cacheVal为更新缓存的值,定义retuanVal为返回给调用方的值:- 缓存命中时:
cacheVal即命中的值,retuanVal有必要的话基于cacheVal包装成Optional - 缓存未命中时:
retuanVal即目标方法的执行结果,cacheVal有必要的话从Optional包装取出
- 缓存命中时:
- 收集所有的
CachePutRequest,这里包括了两种:- 未命中缓存的
CacheableOperation CachePutOperation
- 未命中缓存的
- 执行这些
CachePutRequest,本质上就是Cache#put - 最后执行的是属性
beforeInvocation = false的CacheEvictOperation操作,即在方法执行后剔除缓存(基于allEntries属性决定是清除所有缓存还是指定key的缓存),当然这里就可以支持unless属性判断了(因为有方法的返回值) - 至此就是整个
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对底层缓存操作的基础方法doGetdoPutdoEvictdoClear - 操作过程中的异常由
CacheErrorHandler接口处理,默认是Spring提供的唯一实现SimpleCacheErrorHandler:直接抛出对应异常
总结
这段太干了,但是读都读了就尽可能全都贴上来,抛开实现的细节逻辑,我认为至少有以下几点直观收益:
- 清楚的理解了缓存操作属性的配置方式和优先级原理:
注解上指定的属性 > CacheConfigurer 指定的属性 > 缺省属性,因此可以更加合理的编写配置类 CacheOperationContexts类的设计很有意思,优雅的管理了method -> CacheOperation -> CacheOperationContext的关系CacheInterceptorCacheAspectSupportAbstractCacheInvoker类层级的设计:CacheInterceptor:站在AOP角度专注于Cache Interceptor的实现CacheAspectSupport:负责实现核心的Spring Cache逻辑AbstractCacheInvoker:提供对底层缓存的操作方法
- 等等其他,其实主要用心看,值得学习的地方是很多的
上一篇:【Spring Cache】五 从 @EnableCaching 了解 Spring Cache 实现流程
下一篇:【Spring Cache】七 Spring Cache 流程总结
