目录
SpringCache
缓存
“缓存”是我们日常开发中非常重要的一个环节,是提高产品性能、保证程序稳定最简单粗暴的方法之一。
缓存(Cache)应用在很多地方,比如:操作系统磁盘缓存、Web服务器缓存、客户端浏览器缓存、应用程序缓存、数据库缓存等等。我们此次进行深入了解的,就是应用在Java后端程序缓存的SpringCache。
缓存抽象
JSR107:JCache
说起JSR107或者说是JCache,估计大多数小伙伴都会觉得非常的陌生,没用过且还没听过。JSR107的草案提得其实是非常的早的,但是第一个Final Release版本却一直难产到了2014年,如图(本文截自JSR官网)
虽然最终它还是被作为JSR规范提出了,但那时已经4102年了,黄瓜菜早就凉凉~
在还没有缓存规范出来之前,作为Java市场标准制定的强有力竞争者:Spring框架动作频频,早在2011年就提供了它自己的缓存抽象(Spring3.1)。这一切依托于Spring的良好生态下,各大缓存厂商纷纷提供了实现产品。
因此目前而言,关于缓存这块业界有个通识:
-
Spring Cache缓存抽象已经成了业界实际的标准(几乎所有产品都支持)
-
JSR107仅仅只是官方的标准而已(支持的产品并不多)
-
因为JSR107使用得极少,因此此处对它只做比较简单的一个概念介绍即可。
Spring缓存抽象
上面说了JCache真正发布都到2014年了,而早在2011年Spring3.1版本就定义了它自己的缓存抽象,旨在帮助开发者简化缓存的开发,并且最终流行开来。
这就是我们目前使用的SpringCache。进入源码查看,SpringCache的大部分内容,都在spring-context包内。
还有一部分具体缓存的实现,则在spring-context-support包内,该包内是一些缓存方案的具体实现。当然,还有一部分内容在org.springframework.cache.aspectj包中,该部分内容主要是springcache通过AspectJ方式拦截方法时,一些必要的类。
深入了解
首先,此次源码阅读基于spring-framework 5.1.x版本。在源码阅读的过程中,因为我水平有限,可能有一些表述不太合适,但应该不影响后面的阅读体验,请见谅。
推荐大家阅读时在github上folk一份spring-framework的源码跟着一起看。当然,可以通过folk或clone我的仓库来看https://github.com/Kwin1113/spring-framework/tree/read,在read分支中,对spring-context包内cache部分做了作者注释的翻译,方便大家阅读。
接下来,我们深入看一看spring-context这一部分。我们从包结构来看,org.springframework.cache下几个主要的包为annotation、config、interceptor。在了解这些包内容之前,我们先来关注一下包外的这两个接口,Cache和CacheManager。
- Cache
该接口定义了通用缓存操作的接口,也就是所有缓存实现(RedisCache、EhCache等)都需要实现该接口的这些方法。这个接口定义很好理解,就是操作缓存的基础单位,可以是本地的(本地Map等),也可以是远程的(Redis等)。 - CacheManager
这个接口就更好理解了——缓存管理器,实现该接口的类负责对其下的缓存进行管理。该接口只定义了最最简单但必须的方法,获取缓存和获取缓存名称。
annotation
- annotation包中是一些注解相关的配置类或注解类。
从下图中,我们能看到几个非常熟悉的注解,特别是缓存三大将:@Cacheable、@CachePut和@CacheEvict。
这三大注解的功能想必也不用多说,@EnableCaching自然就是开启注解驱动的缓存功能。@Caching用于在一个类或一个方法上,配置多个基础缓存注解——也就是三大注解。而@CacheConfig可能相对用的比较少,该注解提供了一种在类级别共享常见的缓存相关设置的机制,当此注解出现在给定的类上时,它将为该类中定义的任何缓存操作提供一组默认设置,其中只有四个属性,这四个属性作为类中方法上三大注解缺少某个属性时作为缺省值使用。
2. 接下来看看其中的两个接口和一个抽象类。
- 首先是CachingConfigurer接口:
该类是配置类实现接口,该接口明确地指定在注解驱动的缓存管理中,如何处理缓存和key的生成方法。子类CachingConfigurerSupport实现了该接口的所有基础实现。
通常情况下,不会直接实现该接口,推荐通过继承其子类CachingConfigureSupport来定义配置,此时可以比较方便地仅实现自己所需的配置,而无需实现接口定义的所有方法。具体配置方式可以参考源码中测试类中给出的demo。
- 接着是CacheAnnotationParser接口:
该接口定义了三个方法。
首先是isCandidateClass方法,顾名思义是判断给定类是否满足规定注解格式的缓存操作类。
然后是两个类似的方法,分别是从类或方法上,解析缓存定义,也就是从类或方法上,找到上述四大注解定义。
其在SpringCache中的默认实现为SpringCacheAnnotationParser:
在该实现类中,上面提到的规定的注解格式,只有以下四种。
接口中定义的两个解析缓存定义的方法,最