我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Spring cache源码分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Spring cache源码分析

今天小编给大家分享一下Spring cache源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

题外话:如何阅读开源代码?

有2种方法,可以结合起来使用:

  • 静态代码阅读:查找关键类、方法的usage之处,熟练使用find usages功能,找到所有相关的类、方法,静态分析核心逻辑的执行过程,一步步追根问底,直至建立全貌

  • 运行时debug:在关键方法上加上断点,并且写一个单元测试调用类库/框架,熟练使用step into/step over/resume来动态分析代码的执行过程

核心类图

Spring cache源码分析

如图所示,可以分成以下几类class:

  • Cache、CacheManager:Cache抽象了缓存的通用操作,如get、put,而CacheManager是Cache的集合,之所以需要多个Cache对象,是因为需要多种缓存失效时间、缓存条目上限等

  • CacheInterceptor、CacheAspectSupport、AbstractCacheInvoker:CacheInterceptor是一个AOP方法拦截器,在方法前后做额外的逻辑,也即查询缓存、写入缓存等,它继承了CacheAspectSupport(缓存操作的主体逻辑)、AbstractCacheInvoker(封装了对Cache的读写)

  • CacheOperation、AnnotationCacheOperationSource、SpringCacheAnnotationParser:CacheOperation定义了缓存操作的缓存名字、缓存key、缓存条件condition、CacheManager等,AnnotationCacheOperationSource是一个获取缓存注解对应CacheOperation的类,而SpringCacheAnnotationParser是真正解析注解的类,解析后会封装成CacheOperation集合供AnnotationCacheOperationSource查找

源码分析(带注释解释)

下面对Spring cache源码做分析,带注释解释,只摘录核心代码片段。

1、解析注解

首先看看注解是如何解析的。注解只是一个标记,要让它真正工作起来,需要对注解做解析操作,并且还要有对应的实际逻辑。

SpringCacheAnnotationParser:负责解析注解,返回CacheOperation集合

public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {        // 解析类级别的缓存注解@Overridepublic Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);return parseCacheAnnotations(defaultConfig, type);}        // 解析方法级别的缓存注解@Overridepublic Collection<CacheOperation> parseCacheAnnotations(Method method) {DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());return parseCacheAnnotations(defaultConfig, method);}        // 解析缓存注解private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {Collection<CacheOperation> ops = null;                // 解析@Cacheable注解Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);if (!cacheables.isEmpty()) {ops = lazyInit(ops);for (Cacheable cacheable : cacheables) {ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));}}                // 解析@CacheEvict注解Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);if (!evicts.isEmpty()) {ops = lazyInit(ops);for (CacheEvict evict : evicts) {ops.add(parseEvictAnnotation(ae, cachingConfig, evict));}}                // 解析@CachePut注解Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);if (!puts.isEmpty()) {ops = lazyInit(ops);for (CachePut put : puts) {ops.add(parsePutAnnotation(ae, cachingConfig, put));}}                // 解析@Caching注解Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(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;}

AnnotationCacheOperationSource:调用SpringCacheAnnotationParser获取注解对应CacheOperation

public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {        // 查找类级别的CacheOperation列表@Overrideprotected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {return determineCacheOperations(new CacheOperationProvider() {@Overridepublic Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {return parser.parseCacheAnnotations(clazz);}});}        // 查找方法级别的CacheOperation列表@Overrideprotected Collection<CacheOperation> findCacheOperations(final Method method) {return determineCacheOperations(new CacheOperationProvider() {@Overridepublic Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser) {return parser.parseCacheAnnotations(method);}});}}

AbstractFallbackCacheOperationSource:AnnotationCacheOperationSource的父类,实现了获取CacheOperation的通用逻辑

public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {private final Map<Object, Collection<CacheOperation>> attributeCache =new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);// 根据Method、Class反射信息,获取对应的CacheOperation列表@Overridepublic Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {if (method.getDeclaringClass() == Object.class) {return null;}Object cacheKey = getCacheKey(method, targetClass);Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);                // 因解析反射信息较耗时,所以用map缓存,避免重复计算                // 如在map里已记录,直接返回if (cached != null) {return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);}                // 否则做一次计算,然后写入mapelse {Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);if (cacheOps != null) {if (logger.isDebugEnabled()) {logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);}this.attributeCache.put(cacheKey, cacheOps);}else {this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);}return cacheOps;}}        // 计算缓存操作列表,优先用target代理类的方法上的注解,如果不存在则其次用target代理类,再次用原始类的方法,最后用原始类private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {// Don't allow no-public methods as required.if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {return null;}// The method may be on an interface, but we need attributes from the target class.// If the target class is null, the method will be unchanged.Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);// If we are dealing with method with generic parameters, find the original method.specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);                // 调用findCacheOperations(由子类AnnotationCacheOperationSource实现),最终通过SpringCacheAnnotationParser来解析// First try is the method in the target class.Collection<CacheOperation> opDef = findCacheOperations(specificMethod);if (opDef != null) {return opDef;}// Second try is the caching operation on the target class.opDef = findCacheOperations(specificMethod.getDeclaringClass());if (opDef != null && ClassUtils.isUserLevelMethod(method)) {return opDef;}if (specificMethod != method) {// Fallback is to look at the original method.opDef = findCacheOperations(method);if (opDef != null) {return opDef;}// Last fallback is the class of the original method.opDef = findCacheOperations(method.getDeclaringClass());if (opDef != null && ClassUtils.isUserLevelMethod(method)) {return opDef;}}return null;}

2、逻辑执行

以@Cacheable背后的逻辑为例。预期是先查缓存,如果缓存命中了就直接使用缓存值,否则执行业务逻辑,并把结果写入缓存。

ProxyCachingConfiguration:是一个配置类,用于生成CacheInterceptor类和CacheOperationSource类的Spring bean

CacheInterceptor:是一个AOP方法拦截器,它通过CacheOperationSource获取第1步解析注解的CacheOperation结果(如缓存名字、缓存key、condition条件),本质上是拦截原始方法的执行,在之前、之后增加逻辑

// 核心类,缓存拦截器public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {        // 拦截原始方法的执行,在之前、之后增加逻辑@Overridepublic Object invoke(final MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();                // 封装原始方法的执行到一个回调接口,便于后续调用CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {@Overridepublic Object invoke() {try {                                        // 原始方法的执行return invocation.proceed();}catch (Throwable ex) {throw new ThrowableWrapper(ex);}}};try {                        // 调用父类CacheAspectSupport的方法return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());}catch (CacheOperationInvoker.ThrowableWrapper th) {throw th.getOriginal();}}}

CacheAspectSupport:缓存切面支持类,是CacheInterceptor的父类,封装了所有的缓存操作的主体逻辑

主要流程如下:

  • 通过CacheOperationSource,获取所有的CacheOperation列表

  • 如果有@CacheEvict注解、并且标记为在调用前执行,则做删除/清空缓存的操作

  • 如果有@Cacheable注解,查询缓存

  • 如果缓存未命中(查询结果为null),则新增到cachePutRequests,后续执行原始方法后会写入缓存

  • 缓存命中时,使用缓存值作为结果;缓存未命中、或有@CachePut注解时,需要调用原始方法,使用原始方法的返回值作为结果

  • 如果有@CachePut注解,则新增到cachePutRequests

  • 如果缓存未命中,则把查询结果值写入缓存;如果有@CachePut注解,也把方法执行结果写入缓存

  • 如果有@CacheEvict注解、并且标记为在调用后执行,则做删除/清空缓存的操作

// 核心类,缓存切面支持类,封装了所有的缓存操作的主体逻辑public abstract class CacheAspectSupport extends AbstractCacheInvokerimplements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {        // CacheInterceptor调父类的该方法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,获取所有的CacheOperation列表Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);if (!CollectionUtils.isEmpty(operations)) {                                // 继续调一个private的execute方法执行return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));}}                // 如果spring bean未初始化完成,则直接调用原始方法。相当于原始方法没有缓存功能。return invoker.invoke();}        private的execute方法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, new Callable<Object>() {@Overridepublic Object call() throws Exception {return 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 methodreturn invokeOperation(invoker);}}                // 如果有@CacheEvict注解、并且标记为在调用前执行,则做删除/清空缓存的操作// Process any early evictionsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), true,CacheOperationExpressionEvaluator.NO_RESULT);                // 如果有@Cacheable注解,查询缓存// Check if we have a cached item matching the conditionsCache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));                // 如果缓存未命中(查询结果为null),则新增到cachePutRequests,后续执行原始方法后会写入缓存// Collect puts from any @Cacheable miss, if no cached item is foundList<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();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 hitcacheValue = cacheHit.get();returnValue = wrapCacheValue(method, cacheValue);}else {                        // 缓存未命中、或有@CachePut注解的情况,需要调用原始方法// Invoke the method if we don't have a cache hit                        // 调用原始方法,得到结果值returnValue = invokeOperation(invoker);cacheValue = unwrapReturnValue(returnValue);}                // 如果有@CachePut注解,则新增到cachePutRequests// Collect any explicit @CachePutscollectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);                // 如果缓存未命中,则把查询结果值写入缓存;如果有@CachePut注解,也把方法执行结果写入缓存// Process any collected put requests, either from @CachePut or a @Cacheable missfor (CachePutRequest cachePutRequest : cachePutRequests) {cachePutRequest.apply(cacheValue);}                // 如果有@CacheEvict注解、并且标记为在调用后执行,则做删除/清空缓存的操作// Process any late evictionsprocessCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);return returnValue;}private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {Object result = CacheOperationExpressionEvaluator.NO_RESULT;for (CacheOperationContext context : contexts) {                        // 如果满足condition条件,才查询缓存if (isConditionPassing(context, result)) {                                // 生成缓存key,如果注解中指定了key,则按照Spring表达式解析,否则使用KeyGenerator类生成Object key = generateKey(context, result);                                // 根据缓存key,查询缓存值Cache.ValueWrapper cached = findInCaches(context, key);if (cached != null) {return cached;}else {if (logger.isTraceEnabled()) {logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());}}}}return null;}private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {for (Cache cache : context.getCaches()) {                        // 调用父类AbstractCacheInvoker的doGet方法,查询缓存Cache.ValueWrapper wrapper = doGet(cache, key);if (wrapper != null) {if (logger.isTraceEnabled()) {logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");}return wrapper;}}return null;}

AbstractCacheInvoker:CacheAspectSupport的父类,封装了最终查询Cache接口的逻辑

public abstract class AbstractCacheInvoker {        // 最终查询缓存的方法protected Cache.ValueWrapper doGet(Cache cache, Object key) {try {                        // 调用Spring Cache接口的查询方法return cache.get(key);}catch (RuntimeException ex) {getErrorHandler().handleCacheGetError(ex, cache, key);return null;  // If the exception is handled, return a cache miss}}}

以上就是“Spring cache源码分析”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Spring cache源码分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Spring cache源码分析

今天小编给大家分享一下Spring cache源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。题外话:如何阅读开源代码
2023-06-29

Spring refresh()源码分析

今天小编给大家分享一下Spring refresh()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。正文public
2023-07-05

ThinkPHP 5.1修改Cache源码的示例分析

这篇文章将为大家详细讲解有关ThinkPHP 5.1修改Cache源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。ThinkPHP 5.1 修改 Cache 源码导语最近在学习 THinkPH
2023-06-09

Spring事务源码分析专题(一)JdbcTemplate使用及源码分析

Spring中的数据访问,JdbcTemplate使用及源码分析前言本系列文章为事务专栏分析文章,整个事务分析专题将按下面这张图完成对源码分析前,我希望先介绍一下Spring中数据访问的相关内容,然后层层递进到事物的源码分析,主要分为两个部分JdbcTempl
Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
2019-10-22

Spring源码剖析9:Spring事务源码剖析

转自:http://www.linkedkeeper.com/detail/blog.action?bid=1045
2023-06-02

spring Kafka中的@KafkaListener源码分析

本篇内容主要讲解“spring Kafka中的@KafkaListener源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring Kafka中的@KafkaListener源码分析”
2023-07-05

Spring AOP核心功能源码分析

这篇“Spring AOP核心功能源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring AOP核心功能源码分析
2023-07-05

spring自动注入AutowiredAnnotationBeanPostProcessor源码分析

本篇内容介绍了“spring自动注入AutowiredAnnotationBeanPostProcessor源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔
2023-07-05

Spring Bean生命周期源码分析

这篇“Spring Bean生命周期源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring Bean生命周期源码
2023-07-05

Spring源码解析CommonAnnotationBeanPostProcessor

这篇文章主要为大家介绍了Spring源码解析CommonAnnotationBeanPostProcessor示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Spring MVC策略模式之MethodArgumentResolver源码分析

今天小编给大家分享一下Spring MVC策略模式之MethodArgumentResolver源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,
2023-07-05

Spring之ShutDown Hook死锁现象源码分析

本篇内容主要讲解“Spring之ShutDown Hook死锁现象源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring之ShutDown Hook死锁现象源码分析”吧!Spring
2023-07-05

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录