spring-cache 源码解析(无图)
简介
spring-cache 提供了高效的缓存相关数据存储和删除,通过运用注解@Cachable就可以简单完成从缓存中读取数据,减少在业务代码中加入缓存,读取缓存的操作,相对方便快捷。
用例
spring-boot-starter-cache
spring-boot-starter-data-redis
结合 redis + spring-cache,使用分布式缓存。
解析
依赖信息
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
引入了什么
@EnableCaching
对启动类加上 @EnableCaching 注解
- 引入CachingConfigurationSelector, 加载 ProxyCachingConfiguration 作为代理配置类
- ProxyCachingConfiguration 加载了 BeanFactoryCacheOperationSourceAdvisor
- BeanFactoryCacheOperationSourceAdvisor 使用 cacheInterceptor 作为 advice, 使用 CacheOperationSource 作为pointcut。
- 至此完成了代理类的配置。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
// ...
}
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
// ProxyCachingConfiguration 代理类配置加载为bean
}
@Configuration
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
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() {
...
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor() {
...
}
}
RedisAutoConfiguration
- RedisAutoConfiguration 加载 RedisProperties 属性类
- RedisAutoConfiguration 加载 LettuceConnectionConfiguration、JedisConnectionConfiguration 也就加载了 RedisConnectionFactory
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// 加载restemplate, StringRedisTemplate
}
CacheAutoConfiguration
- 在有CacheAspectSupport这个bean作为条件,加载CacheProperties属性类,导入 CacheConfigurationImportSelector
- CachingConfigurationSelector 会加载 全部的缓存类型配置类,由shouldSkip进行条件过滤,就会剩下 RedisCacheConfiguration 可以被加载成 bean
- RedisCacheConfiguration 由于引入 RedisAutoConfiguration 加载了 RedisConnectionFactory作为bean,所以条件成立,加载 cacheManager
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
// ....
static class CacheConfigurationImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for (int i = 0; i < types.length; i++) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
}
final class CacheConfigurations {
private static final Map<CacheType, Class<?>> MAPPINGS;
static {
Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections.unmodifiableMap(mappings);
}
}
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
ResourceLoader resourceLoader) {
RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
}
return this.customizerInvoker.customize(builder.build());
}
}
总结引入部分
- @EnableCaching 提供 代理类
- spring-boot-starter-data-redis 引入RedisConnectionFactory, 满足redis缓存配置类的condition条件
- spring-boot-starter-cache 引入各种缓存配置, 用于加载 CacheManager
代理类advisor部分
- BeanFactoryCacheOperationSourceAdvisor, 使用 cacheInterceptor 作为 advice, 覆写 invoke方法用于调用拦截
- BeanFactoryCacheOperationSourceAdvisor,使用 CacheOperationSource 作为 pointCut, 覆写 methodMatcher.matches 方法用于 【装载代理类 && 实际调用生成调用链】
- 其中 methodMatcher.matches() 方法就是判断有没有几个注解,并通过 SpringCacheAnnotationParser 解析这些注解成元数据CacheOperation。
- cacheInterceptor 实现了 MethodInterceptor,而MethodInterceptor又实现了 Advice, 因此调用 目标方法时,就会调用到 cacheInterceptor 的 invoke 方法。
- 解析过程,也就是matches识别注解的部分:
@Nullable private Collection<CacheOperation> parseCacheAnnotations( DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) { Collection<CacheOperation> ops = null; Collection<Cacheable> cacheables = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class) : AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class)); if (!cacheables.isEmpty()) { ops = lazyInit(null); for (Cacheable cacheable : cacheables) { ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable)); } } Collection<CacheEvict> evicts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class) : AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class)); if (!evicts.isEmpty()) { ops = lazyInit(ops); for (CacheEvict evict : evicts) { ops.add(parseEvictAnnotation(ae, cachingConfig, evict)); } } Collection<CachePut> puts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class) : AnnotatedElementUtils.findAllMergedAnnotations(ae, CachePut.class)); if (!puts.isEmpty()) { ops = lazyInit(ops); for (CachePut put : puts) { ops.add(parsePutAnnotation(ae, cachingConfig, put)); } } Collection<Caching> cachings = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class) : AnnotatedElementUtils.findAllMergedAnnotations(ae, Caching.class)); if (!cachings.isEmpty()) { ops = lazyInit(ops); for (Caching caching : cachings) { Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching); if (cachingOps != null) { ops.addAll(cachingOps); } } } return ops; }invoke 方法
- contexts.get(CacheEvictOperation.class): 识别CacheEvictOperation,前置缓存删除,默认不删:processCacheEvicts【beforeInvocation() default false】;
- contexts.get(CacheableOperation.class):识别CacheableOperation,从缓存中获取,如果没有则使用get方法+lookup方法找到对应缓存中的数据
- contexts.get(CacheableOperation.class):当命中为空时,识别CacheableOperation加入到请求列表
- 当命中时,包装 returnValue = cacheValue; 当未命中时,执行到cacheInterceptor 注册的 invocation.proceed(); 获取返回值作为 returnValue。cacheValue解包装returnValue
- contexts.get(CachePutOperation.class):识别CachePutOperation,加入到请求列表中
- cachePutRequest.apply(): 将前面的 CacheableOperation、CachePutOperation 请求实际执行。put数据到缓存中
- contexts.get(CacheEvictOperation.class):识别CacheEvictOperation,后置缓存删除,默认删除。即前置删除和后置删除只会成功一个。因为入参true / false, 会用于比较 operation.isBeforeInvocation(), 所以只能成功一个,就没有延时双删。
@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 && !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; }附录
spring-cache,基于aop,需要对aop有些了解,比较容易掌握。
补充一下 spring-aop 加载的一个大致流程- doCreateBean 中会进行 bean的实例化 - 放入3级缓存 - 属性注入 - 初始化,其中初始化就会调用到后置处理器,使用 AbstractAutoProxyCreator.wrapIfNecessary() 完成代理类的加载。 (applyBeanPostProcessorsAfterInitialization,实现就有 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization)
- wrapIfNecessary(), 就会加载容器中的 Advisor.class 类型的对象,然后执行canApply(), 基于 classFilter + 对类上的各个方法对比methodMatcher.matches方法,将满足切点的advisor都作为config加载到代理类中(org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors加载注解@Around等生成 Advisor)
- 调用时,再调用jdk/cglib下的invoke方法。判断本地方法缓存有没有放入这个调用链,如果没有就生成一条调用链,就是通过config里的各个advisor对应的pointcut的methodMatcher.matches方法,返回值决定能不能添加到调用链中。这也就是为什么上面说 BeanFactoryCacheOperationSourceAdvisor,使用 CacheOperationSource 作为 pointCut, 覆写 methodMatcher.matches 方法用于 【装载代理类 && 实际调用生成调用链】的原因。之后将生成的调用链依次调用 MethodInterceptor.invoke,完成能力增强。 (List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); 形成调用链)
本文详细解读了SpringCache的内部工作机制,包括如何通过注解@Cachable简化缓存操作,以及与SpringBoot的Redis集成,重点讲解了AOP代理、BeanFactoryCacheOperationSourceAdvisor的配置和工作原理。
2849

被折叠的 条评论
为什么被折叠?



