@EnableCache
理解Spring的缓存首先从 @EnableCache这个注解说起,这个注解是开启缓存功能的关键,通过源码分析可以知道
@EnableCache 注解 通过 @Import引入 CachingConfigurationSelector ,其向相容其中注册两个Bean, AutoProxyRegistrar以及ProxyCachingConfiguration,下面分别介绍这两个Bean的用处
1> org.springframework.context.annotation.AutoProxyRegistrar 实现了ImportBeanDefinitonRegistar 接口 ,此接口又再次向容器中注册
internalAutoProxyCreator = InfrastructureAdvisorAutoProxyCreator
2> org.springframework.cache.annotation.ProxyCachingConfiguration 是一个Bean定义文件 注册了以下3个实例到容器中
org.springframework.cache.config.internal.CacheAdvisor
org.springframework.cache.interceptor.CacheInterceptor
org.springframework.cache.interceptor.CacheOperationSource
CacheOperationSource介绍
这个类主要 通过getCacheOperations 返回缓存操作(CacheOperation)的集合。我们这里关注他的一个实现类AnnotationCacheOperationSource:作用是获取定义在类和方法上的SpringCache相关的标注并将其转换为对应的CacheOperation属性。 对SpringCache的几个标注@Cacheable、@CachePut、@CacheEvict、@Caching进行了解析,并相应的创建CacheableOperation、CacheEvictOperation、CachePutOperation
AnnotationCacheOperationSource中还有一个CacheOperation的解析器 默认使用 SpringCacheAnnotationParser, 通过解析器将标注的注解解析成 CacheOperation 集合返回
CacheOperation介绍
缓存操作的基类,他的实现类有CacheableOperation、CacheEvictOperation、CachePutOperation
CacheableOperation: 顾名思义,就是执行缓存的put操作
CacheEvictOperation: 执行缓存的Evict 移除操作
CacheableOperation: 缓存的保存操作,和put是由区别的,put时候始终都会调用缓存的方法,而cachebale有缓存直接获取
CacheInterceptor介绍
通过源码查看可知CacheInterceptor 实现了 MethodInterceptor 是一个拦截器,类的继承关系如下:
CacheInterceptor
-> CacheAspectSupport implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton
-> AbstractCacheInvoker
cacheInterceptor中只有一个 invoke 方法,这个方法就是执行缓存相关操作的方法,里面通过invocation.proceed() 执行完整个拦截器链;
从上面可以看出CacheAspectSupport 实现 BeanFactoryAware接口 ,SmartInitializingSingleton接口
InfrastructureAdvisorAutoProxyCreator介绍
InfrastructureAdvisorAutoProxyCreator是一个后置处理器,其继承关系如下:
InfrastructureAdvisorAutoProxyCreator
->AbstractAdvisorAutoProxyCreator
->AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
->ProxyProcessorSupport
从以上可知 SmartInstantiationAwareBeanPostProcessor 是一个后置处理器,返回一个增强后代理对象,其主要是在Bean创建厨师换完成后调用 postProcessAfterInitialization 时返回一个增强代理对象(这点逻辑和AOP完全一样)
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
执行流程:
使用代理对象调用方法时,会被cglib拦截
1、 System.out.println(calculator.div(1,1)); 执行到这里跟踪进去
2、调用了CglibAopProxy.intercept方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// We can skip creating a MethodInvocation: just invoke the target directly.
// Note that the final invoker must be an InvokerInterceptor, so we know
// it does nothing but a reflective operation on the target, and no hot
// swapping or fancy proxying.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
3> 最主要的是红色标注的 proceed方法 ,跟踪进去:ReflectiveMethodInvocation 的proceed方法:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
4> 跟踪进去发现调用的 CacheInterceptor的invoke方法,里面调用了父类的execute方法也就是 CacheAspectSupport的 execute方法
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
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();
}
通过 Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass); 获取到 cacheableOperation; 然后再执行 CacheAspectSupport的另外一个重载方法 execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
5> CacheAspectSupport的另外一个重载方法execute
@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
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 {
return wrapCacheValue(method, cache.get(key, () -> unwrapReturnValue(invokeOperation(invoker))));
}
catch (Cache.ValueRetrievalException ex) {
// The invoker wraps any Throwable in a ThrowableWrapper instance so we
// can just make sure that one bubbles up the stack.
throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();
}
}
else {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
if (cacheHit != null && cachePutRequests.isEmpty() && !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);
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) { //加入缓存
cachePutRequest.apply(cacheValue);
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}