引言
本文只讨论spring-cache在springboot中是如何实现的,具体的使用方法网上有很多,这里就不详细描述了. 本文中需要涉及到这些类,先列举出来.
- @EnableCaching: cache的入口,用于开启spring-cache
- CachingConfigurationSelector 选择使用哪种AbstractCachingConfiguration
- ProxyCachingConfiguration 基于注解的缓存配置
- CachingConfigurerSupport:缓存配置支持类,需要使用spring cache的项目继承,一个项目只能有一个相关bean。
- AbstractCachingConfiguration:抽象缓存配置,用于初始化配置。加载cacheManager、 cacheResolver、 keyGenerator
- ProxyCachingConfiguration : 继承AbstractCachingConfiguration,默认的缓存代理配置,用来配置拦截器。
- CacheInterceptor 缓存的切面拦截器
- CacheAspectSupport 缓存方面的基类,例如CacheInterceptor或AspectJ方面。这使基础的Spring缓存基础结构可以轻松地用于为任何方面系统实现方面
- Cache 缓存的抽象, 定义了缓存的一些操作行为
- CacheManager 缓存管理器,获取缓存的接口
1:启动干了些啥
- 添加@EnableCaching, 这个注解会@Import一个
CachingConfigurationSelector类.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
}
- 而
CachingConfigurationSelector的selectImports方法中会根据注解中的AdviceMode属性去判断到底走PROXY还是ASPECTJ
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
//这里默认走的是PROXY
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}
selectImports方法中又返回了两个类的名称,这里会去加载这两个类
private String[] getProxyImports() {
List<String> result = new ArrayList<>(3);
result.add(AutoProxyRegistrar.class.getName());
result.add(ProxyCachingConfiguration.class.getName());
if (jsr107Present && jcacheImplPresent) {
result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
}
return StringUtils.toStringArray(result);
}
- 先来看下
AutoProxyRegistrar的registerBeanDefinitions方法干了些什么
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) {
//如果mode的模式是PROXY的话则向容器注册一个自动代理创建器,这里是InfrastructureAdvisorAutoProxyCreator实现的
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isInfoEnabled()) {
String name = getClass().getSimpleName();
logger.info(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAutoProxyCreatorIfNecessary(registry, null);
}
@Nullable
public static BeanDefinition registerAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}
可以看出创建了一个InfrastructureAdvisorAutoProxyCreator类型的BeanDefinition 它是一个AbstractAdvisorAutoProxyCreator,默认扫描所有Advisor(切面)的类,判断是否能代理,对能代理的类生成代理.
- 再来看下
ProxyCachingConfiguration
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@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;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
CacheInterceptor interceptor = new CacheInterceptor();
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
interceptor.setCacheOperationSource(cacheOperationSource);
return interceptor;
}
}
可以看出这里实例化了三个bean 分别是BeanFactoryCacheOperationSourceAdvisor, CacheOperationSource, CacheInterceptor.
先来说说BeanFactoryCacheOperationSourceAdvisor, 它继承于AbstractBeanFactoryPointcutAdvisor,作用是在每个bean的初始化时 (每个bean都会被加载成 advised 对象 -> 有 targetSource 和 Advisor[] 数组),每个bean被调用方法的时候都是先遍历advisor的方法,然后在调用原生bean(也就是targetSource)的方法,实现了aop的效果;
bean 加载的时候 BeanFactoryCacheOperationSourceAdvisor 的 getPointcut() 也就是 CacheOperationSourcePointcut 就会被获取,然后调用CacheOperationSourcePointcut.matches()方法, 用来匹配对应的bean, 假设bean在BeanFactoryCacheOperationSourceAdvisor的扫描中 matchs() 方法返回了true, 结果就是在每个bean的方法被调用的时候 CacheInterceptor 中的 invoke() 方法就会被调用
再来CacheOperationSource 是一个接口, 实现类是AnnotationCacheOperationSource, 父类是AbstractFallbackCacheOperationSource
父类AbstractFallbackCacheOperationSource的getCacheOperations方法
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
else {
//这里去找目标方法上的注解,如@Cachable @CachePut 等
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
}
else {
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
}
这里其实就是对attributeCache 的封装使用,通过把 method - CacheOperation
然后在 CacheInterceptor.invoke() 的时候通过invocation 获取到 method-class 然后调用 CacheOperationSource.getCacheOperations() 获取到 CacheOperation,
CacheOperation 其实就是触发对应spring-cache 注解的操作-获取缓存的实现了
最后是AnnotationCacheOperationSource, 它的findCacheOperations()方法, 也就是用来解析是否要拦截该方法(@Cachable @CachePut)等注解标记的方法
启动解析部分就差不多了.下边就是在一个被例如@Cachable注解的方法执行流程了
2:执行流程
- 被注解如@Cachable标记的方法会进入
CacheInterceptor.invoke()方法
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();
}
}
}
- 然后调用父类
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) {
//根据Class+Method获取 Collection<CacheOperation>
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method,
//注意这个对象在创建的时候会将已经确定的Cache等信息构建好。
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
其中new CacheOperationContexts(operations, method, args, target, targetClass)需要注意下,最终创建的这个对象会从CacheManager#getCache方法中根据cacheName找到对应的cache。
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
this.metadata = metadata;
this.args = extractArgs(metadata.method, args);
this.target = target;
//这里去根据找CacheManager中找对应的Cache
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
this.cacheNames = createCacheNames(this.caches);
}
....
protected Collection<? extends Cache> getCaches(
CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) {
//根据CacheResolver去找对应的caches
Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);
if (caches.isEmpty()) {
throw new IllegalStateException("No cache could be resolved for '" +
context.getOperation() + "' using resolver '" + cacheResolver +
"'. At least one cache should be provided per cache operation.");
}
return caches;
}
....
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<String> cacheNames = getCacheNames(context);
if (cacheNames == null) {
return Collections.emptyList();
}
Collection<Cache> result = new ArrayList<>(cacheNames.size());
for (String cacheName : cacheNames) {
//在CacheManager中根据cacheName找到对应的Cache。
Cache cache = getCacheManager().getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named '" +
cacheName + "' for " + context.getOperation());
}
result.add(cache);
}
return result;
}
继续执行
/*
* 这个方法可以看做是处理@Cacheable, @CachePut, @CacheEvict @Caching这些注解的方法
*/
@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, handleSynchronizedGet(invoker, key, cache));
}
catch (Cache.ValueRetrievalException ex) {
// Directly propagate ThrowableWrapper from the invoker,
// or potentially also an IllegalArgumentException etc.
ReflectionUtils.rethrowRuntimeException(ex.getCause());
}
}
else {
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
// 被@CacheEvict注解, 且属性beforeInvocation = true,表示需要在调用前执行清除缓存, 调用Cache的doGet方法或者 //doClear方法
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
// 尝试去缓存中获取数据, 调用Cache的doGet方法
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
// 如果没有命中缓存, 那么就构建需要缓存的数据包装对象,准备在执行真实方法后将数据放入缓存中
List<CachePutRequest> cachePutRequests = new ArrayList<>();
if (cacheHit == null) {
//如果被@Cacheable标记,则需要将返回结果缓存,收集起来
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
//命中了缓存,且不需要缓存结果信息
if (cacheHit != null && !hasCachePut(contexts)) {
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
returnValue = wrapCacheValue(method, cacheValue);
}
else {
// 没有命中缓存, 则执行目标方法
returnValue = invokeOperation(invoker);
cacheValue = unwrapReturnValue(returnValue);
}
// 如果被@CachePut 标记,那么需要缓存,收集起来
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// 如果有需要被放入缓存的数据信息,那么就将其放入缓存, 调用Cache的doPut方法
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(cacheValue);
}
// 被@CacheEvict注解, 且属性beforeInvocation = false, 则在方法调用后执行清除缓存
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
return returnValue;
}
-
上边的doGet, doPut等操作都定义在
org.springframework.cache#Cache接口中,有很多实现,如ConcurrentMapCache,EhCacheCache,RedisCache等等.这些Cache中都自定义了自己的缓存实现. -
我们在用注解式缓存的时候如:
@Cacheable,@CachePut,@CacheEvict的时候, 底层是怎么实现的呢,这里我们拿RedisCache来举个栗子
public class RedisCache extends AbstractValueAdaptingCache {
/*
* 被@Cacheable注解会执行该方法, 从缓存中拿数据.
*/
@Override
@Nullable
public ValueWrapper get(Object key) {
return toValueWrapper(lookup(key));
}
/*
* 放入缓存, 对应@CachePut或者被@Cacheable注解
* @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
*/
@Override
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));
}
cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
}
}
/*
* 清除缓存 对应@CacheEvict
* @see org.springframework.cache.Cache#evict(java.lang.Object)
*/
@Override
public void evict(Object key) {
cacheWriter.remove(name, createAndConvertCacheKey(key));
}