Spring Cache
admin
2024-03-21 00:53:21
0

使用方式

1、 开启缓存能力

org.springframework.bootspring-boot-starter-cache2.1.3.RELEASE

2、在应用启动类添加@EnableCaching注解:

@SpringBootApplication
@EnableCaching
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

3、在业务方法添加@Cacheable等注解

@Cacheable(cacheNames = {"task"})
public TaskInfoDTO getTask(String taskId) {log.info("TestBuzz.getTask mock query from DB......");TaskInfoDTO taskInfoDTO = new TaskInfoDTO();taskInfoDTO.setTaskId(taskId);taskInfoDTO.setApplicantId("system");taskInfoDTO.setDescription("test");return taskInfoDTO;
}

4、模拟请求

@GetMapping("/test_cache")
public IResp testCache() {TaskInfoDTO taskInfoDTO = this.testBuzz.getTask("task123");return IResp.getSuccessResult(taskInfoDTO);
}

最佳实践

扩展性分析

原理解析

关键概念

CachingConfigurer
BeanFactoryCacheOperationSourceAdvisor
CacheOperationSource
CacheInterceptor
CacheAnnotationParser :解析方法、类上的缓存注解转换成CacheOperation

关键接口

缓存配置

spring-boot-autoconfigure有个包叫cache,毫无以为这里就是springboot定义并自动开启缓存配置的地方,该包下基本都是*Configuration类型的类,也就是Springboot自带的缓存相关配置,我们简单分析一下CacheAutoConfiguration/CacheConfigurations/GenericCacheConfiguration/NoOpCacheConfiguration/SimpleCacheConfiguration/CaffeineCacheConfiguration和RedisCacheConfiguration这几个配置类。

@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 {@Bean@ConditionalOnMissingBeanpublic CacheManagerCustomizers cacheManagerCustomizers(ObjectProvider> customizers) {return new CacheManagerCustomizers(customizers.orderedStream().collect(Collectors.toList()));}@Beanpublic CacheManagerValidator cacheAutoConfigurationValidator(CacheProperties cacheProperties, ObjectProvider cacheManager) {return new CacheManagerValidator(cacheProperties, cacheManager);}@Configuration@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)protected static class CacheManagerJpaDependencyConfigurationextends EntityManagerFactoryDependsOnPostProcessor {public CacheManagerJpaDependencyConfiguration() {super("cacheManager");}}
}

/*** {@link ImportSelector} to add {@link CacheType} configuration classes.*/
static class CacheConfigurationImportSelector implements ImportSelector {@Overridepublic 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> MAPPINGS;static {Map> 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);}public static String getConfigurationClass(CacheType cacheType) {Class configurationClass = MAPPINGS.get(cacheType);Assert.state(configurationClass != null, () -> "Unknown cache type " + cacheType);return configurationClass.getName();}public static CacheType getType(String configurationClassName) {for (Map.Entry> entry : MAPPINGS.entrySet()) {if (entry.getValue().getName().equals(configurationClassName)) {return entry.getKey();}}throw new IllegalStateException("Unknown configuration class " + configurationClassName);}
}

代理创建

创建代理工厂,然后选择是否需要直接代理目标类,然后装配增强器,然后调用JdkDynamicAopProxy或者CglibAopProxy创建代理。

参考 Spring AOP 章节。

初始化代理

ProxyCachingConfiguration 复用了父类的能力并且定了AOP的三个核心组件(Pointcut,Advice和Advisor)


@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
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.getNumber("order"));}return advisor;}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public CacheOperationSource cacheOperationSource() {return new AnnotationCacheOperationSource();}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public CacheInterceptor cacheInterceptor() {CacheInterceptor interceptor = new CacheInterceptor();interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);interceptor.setCacheOperationSource(cacheOperationSource());return interceptor;}
}

解析缓存注解

public AnnotationCacheOperationSource(boolean publicMethodsOnly) {this.publicMethodsOnly = publicMethodsOnly;this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
@Override
@Nullable
protected Collection findCacheOperations(Class clazz) {return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
@Override
@Nullable
protected Collection findCacheOperations(Method method) {return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}protected Collection determineCacheOperations(CacheOperationProvider provider) {Collection ops = null;for (CacheAnnotationParser annotationParser : this.annotationParsers) {Collection annOps = provider.getCacheOperations(annotationParser);if (annOps != null) {if (ops == null) {ops = annOps;}else {Collection combined = new ArrayList<>(ops.size() + annOps.size());combined.addAll(ops);combined.addAll(annOps);ops = combined;}}}return ops;
}

AnnotationCacheOperationSource默认构造器使用的是SpringCacheAnnotationParser解析器,解析操作最终委托给SpringCacheAnnotationParser.parseCacheAnnotations,将注解分别解析成对应的操作

保存缓存注解

1、根据 method, targetClass 生成 cacheKey
2、将每个方法的缓存注解解析成 Collection
3、保存在 Map>

public Collection getCacheOperations(Method method, @Nullable Class targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}Object cacheKey = getCacheKey(method, targetClass);Collection cached = this.attributeCache.get(cacheKey);if (cached != null) {return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);}else {// 依次从方法,类,原方法、原类查找缓存注解。遵循短路原理。Collection 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;}
}

缓存拦截

当拦截到调用时,将调用封装成CacheOperationInvoker并交给父类执行,父类CacheAspectSupport实现了SmartInitializingSingleton接口,在单例初始化后容器会调用afterSingletonsInstantiated方法:

1、首先检查是否是同步操作(@Cacheable特性),
1.1、如果是且满足条件缓存,获取逻辑并返回
1.2、否则返回业务逻辑免缓存调用invokeOperation
2、执行@CacheEvict的前置清除(beforeInvocation=true)
3、检查@Cacheable是否命中缓存
3.1、如果没有命中,则放入需要执行CachePutRequest列表暂存
3.2、如果检查缓存命中且没有@CachePut,则返回结果
3.3、否则使用业务查询结果作为返回结果,并且填充需要缓存的结果。
4、收集@CachePut操作,把@CachePut和@Cacheable未命中的请求同步到缓存
5、清理@CacheEvict的缓存(beforeInvocation=false)。


public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {@Override@Nullablepublic 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 {return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());}catch (CacheOperationInvoker.ThrowableWrapper th) {throw th.getOriginal();}}
}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 operations = cacheOperationSource.getCacheOperations(method, targetClass);if (!CollectionUtils.isEmpty(operations)) {return execute(invoker, method,new CacheOperationContexts(operations, method, args, target, targetClass));}}}return invoker.invoke();
}private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {// Special handling of synchronized invocationif (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) {throw (CacheOperationInvoker.ThrowableWrapper) ex.getCause();}}else {return invokeOperation(invoker);}}// 处理 @CacheEvictsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), true,CacheOperationExpressionEvaluator.NO_RESULT);// 是否命中缓存Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));List cachePutRequests = new LinkedList<>();// 缓存没有命中,if (cacheHit == null) {collectPutRequests(contexts.get(CacheableOperation.class),CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);}Object cacheValue;Object returnValue;// 缓存命中,且不是 @CachePutif (cacheHit != null && !hasCachePut(contexts)) {// If there are no put requests, just use the cache hitcacheValue = cacheHit.get();returnValue = wrapCacheValue(method, cacheValue);}else {// 缓存没有命中或 @CachePutreturnValue = invokeOperation(invoker);cacheValue = unwrapReturnValue(returnValue);}collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);// 处理 @CachePutfor (CachePutRequest cachePutRequest : cachePutRequests) {cachePutRequest.apply(cacheValue);}// 处理 @CacheEvictsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);return returnValue;
}

相关内容

热门资讯

安卓系统的如何测试软件,从入门... 你有没有想过,你的安卓手机里那些神奇的软件是怎么诞生的呢?它们可不是凭空出现的,而是经过一系列严格的...
小米8安卓系统版本,安卓系统版... 你有没有发现,手机更新换代的速度简直就像坐上了火箭呢?这不,小米8这款手机自从上市以来,就凭借着出色...
华为手机安卓系统7以上,创新体... 你有没有发现,最近华为手机越来越受欢迎了呢?尤其是那些搭载了安卓系统7.0及以上版本的机型,简直让人...
儿童英语免费安卓系统,儿童英语... 哇,亲爱的家长朋友们,你是否在为孩子的英语学习发愁呢?别担心,今天我要给你带来一个超级好消息——儿童...
ios系统切换安卓系统还原,还... 你有没有想过,有一天你的手机从iOS系统切换到了安卓系统,然后再从安卓系统回到iOS系统呢?这听起来...
灵焕3装安卓系统,引领智能新体... 你知道吗?最近手机圈里可是掀起了一股热潮,那就是灵焕3这款神器的安卓系统升级。没错,就是那个曾经以独...
安卓系统指南针软件,探索未知世... 手机里的指南针功能是不是让你在户外探险时倍感神奇?但你知道吗,安卓系统中的指南针软件可是大有学问呢!...
华为是不用安卓系统了吗,迈向自... 最近有个大新闻在科技圈里炸开了锅,那就是华为是不是不再使用安卓系统了?这可不是一个简单的问题,它涉及...
安卓系统热点开启失败,排查与解... 最近是不是你也遇到了安卓系统热点开启失败的小麻烦?别急,让我来给你详细说说这个让人头疼的问题,说不定...
小米max2系统安卓,安卓系统... 你有没有听说过小米Max2这款手机?它那超大的屏幕,简直就像是个移动的电脑屏幕,看视频、玩游戏,那叫...
电池健康怎么保持安卓系统,优化... 手机可是我们生活中不可或缺的好伙伴,而电池健康度就是它的生命力。你有没有发现,随着使用时间的增长,你...
安卓手机怎么调系统颜色,安卓手... 你有没有发现,你的安卓手机屏幕颜色突然变得不那么顺眼了?是不是也想给它换换“脸色”,让它看起来更有个...
安卓系统清粉哪个好,哪款清粉工... 手机用久了,是不是觉得卡得要命?别急,今天就来聊聊安卓系统清理垃圾哪个软件好。市面上清理工具那么多,...
华为被限制用安卓系统,挑战安卓... 你知道吗?最近科技圈可是炸开了锅!华为,这个我们耳熟能详的名字,竟然因为一些“小插曲”被限制了使用安...
安卓系统是不是外国,源自外国的... 你有没有想过,我们每天离不开的安卓系统,它是不是外国货呢?这个问题听起来可能有点奇怪,但确实很多人都...
安卓系统缺少文件下载,全面解析... 你有没有发现,用安卓手机的时候,有时候下载个文件真是让人头疼呢?别急,今天就来聊聊这个让人烦恼的小问...
kktv系统刷安卓系统怎么样,... 你有没有听说最近KKTV系统刷安卓系统的事情?这可是个热门话题呢!咱们一起来聊聊,看看这个新玩意儿到...
安卓系统连接电脑蓝牙,操作指南... 你有没有遇到过这种情况:手机里堆满了各种好用的应用,可就是想找个方便快捷的方式,把手机里的音乐、照片...
安卓车机11.0系统包,智能驾... 你有没有发现,最近你的安卓车机系统好像悄悄升级了呢?没错,就是那个安卓车机11.0系统包!这可不是一...
安卓系统最高到多少,从初代到最... 你有没有想过,你的安卓手机系统升级到哪一步了呢?是不是好奇安卓系统最高能到多少呢?别急,今天就来带你...