文章目录
1. 使用EnableCaching注解引入组件
根据Spring的官网得知,使用Spring-Cache
的第一步就是要标注@EnableCaching
注解
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching { /***/ }
而EnableCaching
注解使用@Import
注解导入了CachingConfigurationSelector
在使用Spring时,这是一种非常优雅且见名知意的功能打开方式
如果在日常开发中,开发了某一类功能,也可以借鉴Spring的这种方式
自定义@Enablexxx
注解配合@Import
注解
2. CachingConfigurationSelector
CachingConfigurationSelector
继承了AdviceModeImportSelector
覆写了selectImports
方法,该方法返回的String[]
的类全名会被Spring注册到容器中
2.1 selectImports
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
绝大部分的场景都是使用基于代理的模式,而不是织入的模式,所以继续跟踪getProxyImports()
2.2 selectImports
private String[] getProxyImports() {
List<String> result = new ArrayList<>(3);
// 1. 该类用于创建Spring代理
result.add(AutoProxyRegistrar.class.getName());
// 2. 核心类,Spring-Cache配置类
result.add(ProxyCachingConfiguration.class.getName());
// 3. 忽略,用于处理javax.cache.annotation包下的缓存注解的
// 本次源码只分析Spring提供的缓存注解
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}
-
AutoProxyRegistrar
最终会导入AbstractAutoProxyCreator
用于介入bean的生命周期,创建代理对象的,属于Spring-AOP
相关内容
本章只谈论Spring-Cache
的源码,Spring-AOP
的源码可以看这篇文章Spring-AOP源码分析 -
ProxyCachingConfiguration
Spring-AOP
的配置类,引入了Spring-AOP
相关组件到Spring容器中
3. ProxyCachingConfiguration
ProxyCachingConfiguration定义了三个类
- CacheInterceptor
- CacheOperationSource
- BeanFactoryCacheOperationSourceAdvisor
其关系如下
BeanFactoryCacheOperationSourceAdvisor
是一个Advisor
根据Spring-AOP的源码可知,在bean
创建的过程中,会找到Spring容器中所有的Advisor
并且判断是否可以被这个Bean使用
判断时则根据Advisor
的pointcut
的ClassFilter
和MethodFitler
的匹配结果
就不具体去分析是否匹配的逻辑了,具体的表现为,只要bean的类或者方法上定义了
@Cacheable
@Cacheput
@CacheEvict
@Caching
任一注解,这个bean就会被代理
4. CacheInterceptor
根据Spring-AOP
的源码可知,当一个类被创建代理后,该类执行方法前会执行MethodInterceptor
的invoke
方法
而CacheInterceptor
就是一个MethodInterceptor
@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);
}
};
try {
// 1. 第一个参数:一个invoker, 执行invoker的invoke()方法会调用invocation的invoke()方法
// 2. 第二个参数:当前被代理的对象
// 3. 第三个参数:当前正在执行的方法
// 4. 第四个参数:当前正在执行的方法参数
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}
CacheOperationInvoker aopAllianceInvoker = () -> invocation.proceed()
好像什么也没有做,只是调用invocation的invoke方法,那Spring为什么要这样写呢
因为需要兼容不同的代理方式,例如aspectj
织入
可以把aopAllianceInvoker当做invocation
即可,具有执行被代理方法的能力
4.1 execute
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// 1. 获取执行对象的class
Class<?> targetClass = getTargetClass(target);
// 2. 获取CacheOperationSource, 就是在 { ProxyCachingConfiguration } 赋值给当前对象的 { CacheOperationSource }
CacheOperationSource cacheOperationSource = getCacheOperationSource();
// 3. 通过 { CacheOperationSource } 获取 { CacheOperation } 集合
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();
}
5. AnnotationCacheOperationSource
5.1 getCacheOperations
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 1. 构建缓存key
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
// 2. 如果有缓存
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
// 3. 否则调用 { computeCacheOperations } 方法
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
this.attributeCache.put(cacheKey, cacheOps);
return cacheOps;
}
}
5.2 computeCacheOperations
@Nullable
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// 1. 获取最"具体"的方法 : 例如方法是定义在接口上, 或者抽象类上, 那么找到具体实现的方法对象
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// 2. 从方法上找是否有"缓存"注解
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}
// 3. 和第二步的逻辑大同小异, 从类上是否有"缓存"注解
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
// 4. 找不到, 返回空
return null;
}
5.3 findCacheOperations
protected Collection<CacheOperation> findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
findCacheOperations
方法也没做啥事,而是委托给了parser
parser
只有一个实现类,那就是SpringCacheAnnotationParser
5.4 SpringCacheAnnotationParser#parseCacheAnnotations
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
// 1. 获取类上 | 方法上的所有注解
Collection<? extends Annotation> anns = (localOnly ?
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
final Collection<CacheOperation> ops = new ArrayList<>(1);
// 2. 遍历注解
// 2.1 找到@Cacheable注解, 封装成 { CacheableOperation }, 放入 { ops } 集合中
anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
// 2.2 找到@CacheEvict注解, 封装成 { CacheEvictOperation }, 放入 { ops } 集合中
anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
// 2.3 找到@CachePut注解, 封装成 { CachePutOperation }, 放入 { ops } 集合中
anns.stream().filter(ann -> ann instanceof CachePut).forEach(
ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
// 2.4 找到@Caching注解, 放入 { ops } 集合中
// ps: Caching是多个 { @Cacheable }, { @CacheEvict }, { @CachePut }的集合
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
此时,CacheOperation的查找过程就完成了
继续回到CacheInterceptor#execute
方法
6. CacheInterceptor
6.1 execute
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// 1. 获取执行对象的class
Class<?> targetClass = getTargetClass(target);
// 2. 获取CacheOperationSource, 就是在 { ProxyCachingConfiguration } 赋值给当前对象的 { CacheOperationSource }
CacheOperationSource cacheOperationSource = getCacheOperationSource();
// 3. 通过 { CacheOperationSource } 获取 { CacheOperation } 集合
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
// 以上是4.1分析过的
// =====================================================================
// 4. 如果operations不为空, 那么调用重载的 { execute } 方法, 并且创建 { CacheOperationContexts }
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
// 5. 如果为空, 证明当前执行的方法不需要执行增强, 直接执行被代理方法
return invoker.invoke();
}
6.2 CacheOperationContexts
在阅读重载的execute
方法前, 先看下CacheOperationContexts
的创建过程
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
Object[] args, Object target, Class<?> targetClass) {
// 1. 初始 { contexts } 集合对象
this.contexts = new LinkedMultiValueMap<>(operations.size());
// 2. 将 { CacheOperation } 封装为 { CacheOperationContext }
for (CacheOperation op : operations) {
this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
}
// 3. 不常用, 忽略
this.sync = determineSyncFlag(method);
}
PS: 这里和Netty的
ChannelHandlerContext
有点相似
- Netty是将Handler包装为
ChannelHandlerContext
- Spring将
CacheOperation
包装为CacheOperationContext
相同点在于:都是为原有的对象做增强, 使其可以获取更多的信息, 且都使用了链表结构
Netty是通过prev
和next
指针维护了链表
而Spring直接使用了LinkedMultiValueMap
继续跟踪getOperationContext
方法
ps: 这一块涉及到
@Cachexxx
注释上各种属性的解析,比较细枝末节,但这些被解析的属性待会又要用上
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);
}
CacheOperationContext
主要包含了以下属性
class CacheOperationContext {
// 1. CacheOperationMetadata
private final CacheOperationMetadata metadata;
// 2. 执行当前方法的参数
private final Object[] args;
// 3. 执行当前方法的对象
private final Object target;
// 4. 缓存集合
private final Collection<? extends Cache> caches;
}
CacheOperationMetadata
主要包含了以下属性
class CacheOperationMetadata {
// 1. CacheOperation
private final CacheOperation operation;
// 2. 当前执行的方法
private final Method method;
// 3. 当前执行方法的对象类
private final Class<?> targetClass;
// 4. 缓存key的生成器
private final KeyGenerator keyGenerator;
// 5. 用于解析出具体的 { Cache } 对象
private final CacheResolver cacheResolver;
}
继续跟踪getCacheOperationMetadata
方法
看下CacheOperationMetadate
是如何通过注解的属性解析成对象的
protected CacheOperationMetadata getCacheOperationMetadata(
CacheOperation operation, Method method, Class<?> targetClass) {
KeyGenerator operationKeyGenerator;
// 1. 如果注解声明了 "keyGenerator" 属性
if (StringUtils.hasText(operation.getKeyGenerator())) {
// 通过 { BeanName } 从容器中获取具体的 { KeyGenerator } 镀锡
operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
}
else {
// 否则使用默认的
operationKeyGenerator = getKeyGenerator();
}
CacheResolver operationCacheResolver;
// 2. 如果注解声明了 "cacheResolver" 属性
if (StringUtils.hasText(operation.getCacheResolver())) {
operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
}
// 2.1 else if 注解声明了 "CacheManager" 属性
else if (StringUtils.hasText(operation.getCacheManager())) {
CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
operationCacheResolver = new SimpleCacheResolver(cacheManager);
}
// 2.2 都没有声明, 那么使用默认的 { CacheResolver }
else {
operationCacheResolver = getCacheResolver();
Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
}
// 3. 创建MetaData对象并且返回
metadata = new CacheOperationMetadata(operation, method, targetClass,
operationKeyGenerator, operationCacheResolver);
return metadata;
}
6.3 SpringBoot下的CacheManager
在使用Spring-Boot的情况下,我们只需要通过一个属性,就可以向Spring容器中注入默认的CacheManager
spring:
cache:
type: redis
例如这样声明, Spring就会向容器中注入RedisCacheManager
如此:即使我们没有在注解上声明CacheResovler和CacheManager也不会抛出异常
6.4 回到getOperationContext
方法
protected CacheOperationContext getOperationContext(
CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
// 1. 解析metadata
CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
// 2. 创建CacheOperationContext
return new CacheOperationContext(metadata, args, target);
}
6.5 回到execute
方法
此时,CacheOperationContexts
已经创建好了,接而又调用了重载的execute
方法
execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));
这个execute方法是最核心的,包含了Spring-Cache的完整流程
@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)
// 1. 处理@CacheEvict, 在方法执行 [前] 使缓存过期, 具体看 <6.5.1>
// 注意这个: contexts.get(CacheEvictOperation.class), 下面还会被多次调用
// contexts是一个Map, key是 Class<extends CacheOperation>, value就是对应的CacheOperation
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// 2. 处理@Cacheable注解, 具体看 <6.5.2>
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
List<CachePutRequest> cachePutRequests = new LinkedList<>();
// 3. cacheHit == null的几种情况
// - 方法没有使用@Cacheable注解
// - 方法没有执行过
// - 方法的缓存失效了
if (cacheHit == null) {
// 4. 那么收集方法上的@Cacheable注解, 将其转换为 { CachePutRequest }
// 并且放入到 { cachePutRequests } 集合中, 具体看 <6.5.3>
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
// 4. 可以进到if, 那么表示,方法上使用了 `@Cacheable`注解, 并且已经缓存过结果了
// 那么直接将缓存的结果返回
if (cacheHit != null && !hasCachePut(contexts)) {
// 得到缓存的value
cacheValue = cacheHit.get();
// 包装value, 用于返回
// wrapCacheValue: 处理返回类型是 { java.util.Optional } 的情况
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// 5. 如果没有缓存的结果可以返回, 那么执行方法!!!
returnValue = invokeOperation(invoker);
// unwrapReturnValue: 处理返回类型是 { java.util.Optional } 的情况
cacheValue = unwrapReturnValue(returnValue);
}
// 6. 找到方法上的 { @CachePut } 注解对应的 { CachePutContext }
// 将其转换为 { CachePutRequest }, 并且放入 { cachePutRequests }集合中
// 和 <6.5.3> 调用的同一个方法
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// 7. 处理 { cachePutRequests } 集合, 将方法执行结果放入缓存中
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// 8. 处理@CacheEvict, 在方法执行 [后] 使缓存过期
// 和 <6.5.1> 调用的同一个方法
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
6.5.1 processCacheEvicts
当前调用发生在执行方法前
只有将@CacheEvict
的beforeInvocation
声明为true才会执行
private void processCacheEvicts(
Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
for (CacheOperationContext context : contexts) {
CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
// 使缓存失效
performCacheEvict(context, operation, result);
}
}
}
6.5.1 findCachedItem
@Cacheable
注解的作用:
如果方法之前执行过,那么将方法的执行结果放入缓存,下次调用方法时直接返回缓存的内容
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
// 遍历全部的@Cacheable注解
for (CacheOperationContext context : contexts) {
// 是否可以通过注解上的 { condition属性 }
if (isConditionPassing(context, result)) {
// 通过 { KeyGenerator } 生成缓存key
Object key = generateKey(context, result);
// 通过 缓存key 获取缓存的内容
Cache.ValueWrapper cached = findInCaches(context, key);
if (cached != null) {
return cached;
}
}
}
return null;
}
6.5.3 collectPutRequests
这个方法的作用是:将CacheableContext转换为CachePutRequest,并且放入到putRequests
集合中
那为什么要转换一次呢?
原因很简单,因为被
@Cacheable
注解的方法,方法的返回结果需要被缓存起来
并且,被@CachePut
注解的方法,方法的结果也需要被缓存起来
Spring为了保证接下来代码的一致性,在此处将
CacheableOperation
转换为了CachePutRequest
private void collectPutRequests(Collection<CacheOperationContext> contexts,
@Nullable Object result, Collection<CachePutRequest> putRequests) {
for (CacheOperationContext context : contexts) {
// 通过 { condition }
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
// 转换为 { CachePutRequest }
// 放入 { putRequests } 集合中
putRequests.add(new CachePutRequest(context, key));
}
}
}
7. 总结
- 首先,通过
EnableCaching
注解引入CachingConfigurationSelector
CachingConfigurationSelector
注入ProxyCachingConfiguration
到容器中ProxyCachingConfiguration
将BeanFactoryCacheOperationSourceAdvisor
注入到容器中BeanFactoryCacheOperationSourceAdvisor
基于Spring-AOP
创建代理对象- 代理对象执行方法时被
CacheInterceptor
拦截 CacheInterceptor
将@Cacheable
@CacheEvict
@CachePut
转换为对应的CacheOperationContext
CacheInterceptor
在execute()
方法中基于CacheOperationContext
介入方法的执行流程,处理各种缓存逻辑