美文网首页互联网科技
整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

作者: java架构师联盟 | 来源:发表于2020-12-05 21:04 被阅读0次

    缓存技术是一个让所有开发人员又爱又恨的技术,我们爱缓存是因为缓存能给我们带来数量级的响应和流量,但是最迷人的反而最危险,如果缓存用不好也是灾难级别的,特别是一些涉及到公司主要现金流的业务,如果因为我们使用缓存不当,而带给公司一定的损失,不亚于删库跑路的那个大兄弟,那今天我们就来看一下springboot的缓存都有那些东西,学习嘛,一点点的来,慢慢积累自己的经验,才能厚积薄发

    个人公众号:Java架构师联盟,每日更新技术好文

    一、JSR107缓存规范

    为了缓存开发规范的统一,以及提升系统的扩展性,J2EE发布了JSR107缓存规范。 主要是Java Caching定义了5个接口,分别是CachingProvider、CacheManager、Cache、Entry、Expiry。

    下面我们分开详细的展开看一下

    • CachingProvider:

    • 可以创建、配置、获取、管理和控制多个CacheManager,一个Application在运行期间可以访问多个CachingProvider。

    • CacheManager:

    • 可以创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。

    • Cache:

    • 是一个类似于Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。

    • Entry:

    • 是存储在Cache中的Key-Value对。

    • Expiry:

    • 每一个缓存在Cache中的条目有一个定义的有效期,一旦超过这个时间,该条目就为过期状态,一旦过期,条目将不可访问、更新和删除。其中缓存的有效期可以通过ExpiryPolicy设置。

    • 如果说这样讲解让你有点蒙圈的话,那没关系,我们看下面这张图

    整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

    简单总结一下就是:一个应用里面可以有多个缓存提供者(CachingProvider),一个缓存提供者可以获取到多个缓存管理器(CacheManager),一个缓存管理器管理着不同的缓存(Cache),缓存中是一个个的缓存键值对(Entry),每个entry都有一个有效期(Expiry)。缓存管理器和缓存之间的关系有点类似于数据库中连接池和连接的关系。

    二、SpringBoot缓存抽象

    在我自己看来,没有源码所有的理论讲解,都是空谈,或者说就是扯淡,所以我们来看一下,缓存的源码级操作

    Spring从3.1版本开始定义了org.springframework.cache.CacheManagerorg.springframework.cache.Cache接口来统一不同的缓存技术,并支持使用JSR-107注解简化开发。 在IDEA中,使用Spring Initializr快速创建Spring Boot项目时,勾选中Cache后,在配置文件中配置debug=true,可以查看Spring Boot的自动配置项。 其中关于缓存的配置类如下:

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6041" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
    org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
    org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
    org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
    org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
    org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
    org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
    org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
    org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
    org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration
    org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration</pre>

    启动项目后,可以在控制台看到匹配到的只有SimpleCacheConfiguration这个自动配置类,而在SimpleCacheConfiguration类中,使用@Bean注解给容器中注册了一个CacheManager,由此可看Spring Boot默认的CacheManager是ConcurrentMapCacheManager。

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6043" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> SimpleCacheConfiguration matched:

    • Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
    • @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)</pre>

    同样的,我们通过一张图形象的展示一下看看

    整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

    几个重要概念和缓存注解:

    整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

    进入@Caching的源码可以看到,在组合注解内可以使用cacheable、put、evict

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6051" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public @interface Caching {
    Cacheable[] cacheable() default {};

    CachePut[] put() default {};

    CacheEvict[] evict() default {};
    }</pre>

    @Caching的使用

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6053" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> @Caching(
    cacheable = {
    @Cacheable(key = "#name")
    },
    put = {
    @CachePut(key = "#result.id"),
    @CachePut(key = "#result.cNo")
    }
    )</pre>

    @Cacheable、@CachePut、@CacheEvict中的主要参数

    key

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6056" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">#缓存的key,可以为空,也可以使用SpEL表达式编写
    例:@Cacheable(value=“stu”,key=“userName”)</pre>

    condition

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6058" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">#缓存的条件,可以为空,也可以使用SpEL表达式编写,只有为true才缓存/清除缓存,

    不管方法执行前后都可以判断

    例:@Cacheable(value=“stu”,condition=“userName.length()>2”)</pre>

    unless

    <pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n6060" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(119, 119, 119); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">#用于否定缓存,只在方法执行之后判断,也可以使用SpEL表达式编写

    true不缓存,false才缓存

    例:@Cacheable(value=“stu”,unless=“userName == null”)</pre>

    • @Cacheable

    • 标注的方法执行之前,先查看缓存中有没有这个数据,默认按照参数的值作为key去缓存中查找。如果没有就运行这个方法并将方法的执行结果放入缓存中,之后再调用该方法时,直接使用缓存中的数据即可。

    • @CachePut

    • 标注的方法必须要执行,它的运行时机是,先调用目标方法,然后将目标方法的结果放入缓存中,但是更新缓存中的数据时,要注意key值,否则缓存中的数据无法更新。

    • @CacheEvict

    • 这个注解中allEntries = true代表要清除某个缓存中的所有数据。beforeInvocation = false代表缓存的清除在方法执行之后执行,如果出现异常等情况,则不会清除缓存中的数据。这是@CacheEvict

    • 注解默认的。beforeInvocation = true代表缓存的清除在方法执行之前执行,出现异常等情况,也会清除缓存中的数据。

    • key的生成策略

    • key的生成默认使用SimpleKeyGenerator生成的,而SimpleKeyGenerator的生成策略有:如果没有参数:key=new SimpleKey();如果有一个参数:key=参数的值如果有多个参数:key=new SimpleKey(params);

    相关文章

      网友评论

        本文标题:整天都在讨论使用SpringBoot,可你居然连缓存都不清楚

        本文链接:https://www.haomeiwen.com/subject/aazgwktx.html