【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
- 关于
MethodInterceptor
MethodInvocation
等更多细节,可以参考下文:
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
未提供自定义配置时默认实例为基于自定义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<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
)获取目标方法上的所有CacheOperationSource
s - 然后基于这些
CacheOperationSource
s 创建对应的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
为目标方法上每种操作维护对应的一组CacheOperationContext
s,毕竟同一个缓存操作注解可以重复添加(但这样不支持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
的实现逻辑了:
- 首先如果之前构造
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
对底层缓存操作的基础方法doGet
doPut
doEvict
doClear
- 操作过程中的异常由
CacheErrorHandler
接口处理,默认是Spring
提供的唯一实现SimpleCacheErrorHandler
:直接抛出对应异常
总结
这段太干了,但是读都读了就尽可能全都贴上来,抛开实现的细节逻辑,我认为至少有以下几点直观收益:
- 清楚的理解了缓存操作属性的配置方式和优先级原理:
注解上指定的属性 > CacheConfigurer 指定的属性 > 缺省属性
,因此可以更加合理的编写配置类 CacheOperationContexts
类的设计很有意思,优雅的管理了method -> CacheOperation -> CacheOperationContext
的关系CacheInterceptor
CacheAspectSupport
AbstractCacheInvoker
类层级的设计:CacheInterceptor
:站在AOP
角度专注于Cache Interceptor
的实现CacheAspectSupport
:负责实现核心的Spring Cache
逻辑AbstractCacheInvoker
:提供对底层缓存的操作方法
- 等等其他,其实主要用心看,值得学习的地方是很多的