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;
}

相关内容

热门资讯

怎么解除订阅安卓系统,安卓系统... 你是不是也和我一样,手机里订阅了好多服务,结果现在想解除订阅,却一头雾水?别急,今天就来手把手教你如...
安卓系统停用怎么开启,轻松恢复... 亲爱的手机控们,你是否曾经遇到过安卓系统突然停用的情况,让你手忙脚乱,不知所措?别担心,今天就来教你...
安卓系统电池健康度,电池健康度... 你有没有发现,你的安卓手机最近是不是有点儿不给力了?电池续航能力大不如前,充电速度也慢了不少?别急,...
安卓系统按键怎么截图,安卓系统... 你是不是也和我一样,有时候想截个图分享给朋友,却发现安卓手机的截图功能有点神秘呢?别急,今天就来手把...
购票系统安卓源代码,架构设计与... 你有没有想过,那些我们每天离不开的购票系统,它们背后的秘密是什么呢?今天,就让我带你一探究竟,揭开购...
安卓手机系统后台测试,深度解析... 你有没有发现,你的安卓手机后台总是悄悄地忙碌着?别小看了这些后台程序,它们可是手机系统稳定运行的关键...
安卓系统重启的图标,解锁设备新... 手机突然重启,是不是心里有点慌?别急,今天就来和你聊聊安卓系统重启的图标,让你一眼就能认出它,再也不...
车载智慧屏安卓系统,智能出行新... 你有没有发现,现在的车载智慧屏越来越智能了?尤其是那些搭载了安卓系统的,简直就像是个移动的小电脑,不...
安卓系统连上网权限,解锁设备无... 你有没有发现,你的安卓手机里有些应用总是偷偷连上网?别小看这个小小的网络权限,它可是能影响你隐私、消...
安卓谷歌操作系统,探索安卓谷歌... 你知道吗?在智能手机的世界里,有一个操作系统可是无人不知、无人不晓,那就是安卓谷歌操作系统。它就像一...
安卓系统手写%怎样调出,具体实... 你有没有遇到过这种情况:在使用安卓手机的时候,突然想用手写输入法来记录一些灵感或者重要信息,可是怎么...
安卓手机重置 系统设置,轻松恢... 手机用久了是不是感觉卡顿得厉害?别急,今天就来教你怎么给安卓手机来个大变身——重置系统设置!想象你的...
win如何安装安卓系统,Win... 哇,你有没有想过,让你的Win系统也能玩转安卓应用?没错,就是那种在手机上轻松自如的安卓系统,现在也...
苹果qq和安卓系统,跨平台体验... 你有没有发现,现在手机市场上,苹果和安卓的较量可是越来越激烈了呢!咱们就来聊聊这个话题,看看苹果QQ...
显示最好的安卓系统,探索最新旗... 你有没有想过,为什么安卓系统那么受欢迎呢?它就像一个魔法盒子,里面装满了各种神奇的魔法。今天,就让我...
安卓app怎么降级系统,系统版... 你有没有发现,有时候安卓手机的系统更新后,新功能虽然炫酷,但老系统用起来更顺手呢?别急,今天就来教你...
雷军脱离安卓系统,引领科技变革... 你知道吗?最近科技圈可是炸开了锅,因为我们的雷军大大竟然宣布要脱离安卓系统,这可真是让人大跌眼镜啊!...
安卓系统自动开网络,安卓系统自... 你有没有发现,手机里的安卓系统有时候会自动开启网络连接,这可真是让人又爱又恨啊!有时候,你正专心致志...
安卓系统怎样控制后台,因为服务... 手机里的安卓系统是不是感觉越来越卡了?后台程序太多,不仅耗电还影响性能。别急,今天就来教你怎么巧妙地...
安卓系统打游戏推荐,一触即达! 你有没有发现,现在手机游戏越来越好玩了?不管是休闲小游戏还是大型MMORPG,都能在手机上畅玩。但是...