首页 > 其他分享 >Spring Cache + Caffeine的整合与使用

Spring Cache + Caffeine的整合与使用

时间:2023-12-14 12:00:46浏览次数:24  
标签:缓存 String Spring Cache Caffeine param key 方法 public

前言

对于一些项目里需要对数据库里的某些数据一直重复请求的,且这些数据基本是固定的,在这种情况下,可以借助简单使用本地缓存来缓存这些数据。这些介绍一下Spring Cache和Caffeine的使用。

引入依赖和CacheConfig

在pom文件里面引入下面的依赖:

    <dependency>
      <groupId>com.github.ben-manes.caffeine</groupId>
      <artifactId>caffeine</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

在启动类上加上@EnableCaching的注解

@EnableCaching
public class SpringBootApplication{

}

新建一个CacheConfig类

@Configuration
public class CacheConfig {

    /********************************
     *  @function  : 生成缓存管理器
     *  @parameter : []
     *  @return    : org.springframework.cache.CacheManager
     *  @date      : 2023/12/13 14:46
     ********************************/
    @Primary
    @Bean("customCacheManager")
    public CacheManager customCacheManager() {
        SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
        List<Cache> cacheList = new ArrayList<>();
        cacheList.add(customCache());
        simpleCacheManager.setCaches(cacheList);
        return simpleCacheManager;
    }

    /********************************
     *  @function  : 生成自定义缓存容器
     *  @parameter : []
     *  @return    : org.springframework.cache.Cache
     *  @date      : 2023/12/13 14:46
     ********************************/
    public Cache customCache() {
        return new CaffeineCache("customCache", Caffeine.newBuilder()
                .build(), true);
    }
}

这里customCache()方法我并没有设置相关过期时间和最大值,不设置会导致没有默认过期时间和最大值。如果需要设置可以参考下面的写法

    public Cache customCache() {
        return new CaffeineCache("customCache", Caffeine.newBuilder()
                .maximumSize(100)
                .initialCapacity(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .recordStats()
                .build(),
                true);
    }

CaffeineCache参数的讲解

  1. "customCache": 这是缓存的名称。在应用程序中,你可以通过这个名称来获取对应的缓存实例。
  2. Caffeine.newBuilder(): 这是创建Caffeine缓存实例的起始点。newBuilder()返回一个Caffeine构建器对象,用于配置和定制缓存的各种属性。
  3. .maximumSize(100): 这是设置缓存的最大容量,即缓存可以容纳的最大条目数。在这个例子中,缓存的最大容量被设置为100。
  4. .initialCapacity(100): 这是设置缓存的初始容量,即在缓存初始化时分配的内部数据结构的初始大小。在这个例子中,初始容量被设置为100。
  5. .expireAfterWrite(10, TimeUnit.MINUTES): 这是设置缓存项在被写入后的过期时间。在这个例子中,缓存项将在被写入后的10分钟内过期。
  6. .recordStats(): 这是启用缓存统计信息的选项。启用后,你可以从缓存实例中获取有关缓存使用情况的统计信息,例如命中率、加载次数等。

使用中,对过期策略的使用会比较重要,对于过期的策略有:

  1. 写入后过期 (expireAfterWrite): 缓存项被写入后的一段时间内过期。可以通过以下方式配置:

    Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();
    

    在上述示例中,缓存项将在被写入后的10分钟内过期。

  2. 访问后过期 (expireAfterAccess): 缓存项在一段时间内没有被访问后过期。可以通过以下方式配置:

    Caffeine.newBuilder()
            .expireAfterAccess(15, TimeUnit.MINUTES)
            .build();
    

    在上述示例中,缓存项将在最后一次访问后的15分钟内过期。

  3. 定时过期 (expireAfter): 缓存项在指定的固定时间内过期,不考虑写入或访问。可以通过以下方式配置:

    Caffeine.newBuilder()
            .expireAfter(1, TimeUnit.HOURS)
            .build();
    

    在上述示例中,缓存项将在创建后的1小时内过期。

这些过期定时策略可以根据具体的使用场景和需求进行组合或选择。

上面不同写法将会导致生成不同的localcache实现类,可以在build方法中看到:

image-20231213211003206

进入isBounded()方法:

image-20231213211041692

如果使用缓存会调用localcache的get方法,最后进入computeIfAbsent()方法,对比上面两个实现类的实现,先是BoundedLocalCache:

image-20231213211342736

UnboundedLocalCache:

image-20231213211443944

下面这个并不会去检查是否过期。

使用示范

在MVC的使用,可以将缓存的注解标识于service层:

@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {

    @Cacheable(key = "#root.methodName + #sendCardName")
    public int getSlotCount(String sendCardName) {
        ..方法体
        return calCard.getSlotCount();
    }
    
    ...
        
	@CachePut(key = "#param")
	public String updateCache(String param) {
    	// 对数据库更新某个值
  	   return updatedValue;
	}

    @CacheEvict(key = "#param")
    public void evictCache(String param) {
        // 对数据库删除某个值
    }
    
}

使用到的注解解析:

@Cacheable 是Spring框架中用于声明缓存规则的注解之一。它通常用于标记在方法上,以指示Spring在执行方法前先检查缓存,如果缓存中已有数据,则直接返回缓存中的数据,而不执行方法体。如果缓存中没有数据,则执行方法体,并将方法的返回值存入缓存。

以下是 以@Cacheable 注解为例的主要参数介绍和使用方式:

  1. value(或 cacheNames): 指定缓存的名称,可以指定一个或多个缓存。如果指定多个缓存,Spring会依次检查缓存,直到找到第一个有数据的缓存或全部检查完毕。示例:

    @Cacheable(value = "myCache")
    public String getCachedData() {
        // 方法体
    }
    
  2. key 指定缓存项的键。默认情况下,Spring会使用方法的参数作为键,但你也可以通过 key 属性指定自定义的缓存键。示例:

    @Cacheable(value = "myCache", key = "#param")
    public String getCachedData(String param) {
        // 方法体
    }
    
  3. condition 指定条件表达式,只有当条件满足时才会缓存。示例:

    @Cacheable(value = "myCache", condition = "#result != null")
    public String getCachedData() {
        // 方法体
    }
    
  4. unless 指定一个条件表达式,当条件为 true 时,不会将结果放入缓存。示例:

    @Cacheable(value = "myCache", unless = "#result == null")
    public String getCachedData() {
        // 方法体
    }
    
  5. keyGenerator 指定自定义的缓存键生成器。这个属性允许你提供一个实现了 org.springframework.cache.interceptor.KeyGenerator 接口的类,用于生成缓存键。示例:

    @Cacheable(value = "myCache", keyGenerator = "customKeyGenerator")
    public String getCachedData() {
        // 方法体
    }
    
  6. sync 是否启用同步模式。如果设置为 true,可以解决并发查的问题,Spring会在调用方法时锁定缓存,防止多个线程同时访问数据库。默认为 false。示例:

    @Cacheable(value = "myCache", sync = true)
    public String getCachedData() {
        // 方法体
    }
    

这些是 @Cacheable 注解的一些常用参数。可以根据实际需要选择合适的参数来定义缓存规则。

在Spring中,除了 @Cacheable,另外一些注解及其简要介绍:

  1. @CacheEvict 用于从缓存中移除数据。通常用于在方法执行后清空指定缓存。示例:

    @CacheEvict(value = "myCache", key = "#param")
    public void evictCache(String param) {
        // 方法体
    }
    
  2. @CachePut 用于将方法的返回值更新到缓存中,常用于更新缓存而不影响方法的执行。示例:

    @CachePut(value = "myCache", key = "#param")
    public String updateCache(String param) {
        // 方法体
        return updatedValue;
    }
    
  3. @Caching 用于将多个缓存相关的注解组合在一起,实现复杂的缓存操作。示例:

    @Caching(
        evict = {@CacheEvict(value = "cache1", key = "#param1")},
        put = {@CachePut(value = "cache2", key = "#param2")}
    )
    public String complexCacheOperation(String param1, String param2) {
        // 方法体
    }
    
  4. @CacheConfig 用于在类级别配置缓存的一些公共属性,避免在每个方法上都重复指定相同的缓存名称等信息。示例:

    @CacheConfig(cacheNames = "commonCache")
    public class MyService {
        @Cacheable
        public String getCachedData(String param) {
            // 方法体
        }
    }
    

这些注解可以单独使用,也可以结合使用,以满足不同的缓存需求。

清空缓存的方法

清空所有缓存,可以不指定 valuekey,如下所示:

@CacheEvict(allEntries = true)
public void evictAllCaches() {
    // 方法体
}

在这个例子中,allEntries = true 表示清空所有缓存。

如果你想根据某个条件来判断是否清空缓存,可以使用 condition 属性,例如:

@CacheEvict(value = "myCache", key = "#param", condition = "#param != 'noEviction'")
public void evictCacheConditionally(String param) {
    // 方法体
}

在上述例子中,只有当 param 不等于 'noEviction' 时才会执行缓存清空操作。

除了 @CacheEvict,在一些特定场景下,@CachePut 也可以被用来“清空”缓存,因为它将方法的返回值放入缓存,如果返回值为 null,相当于移除缓存项。这种方式通常在更新操作时使用。

注意事项

如下图代码所示,如果在updateCache方法又调用了同个类里面的getSlotCount()方法,是不会使用到缓存的,这是因为缓存的实现是通过AOP实现,在同个类里面调用方法,实际是通过this来调,不会调用到代理对象,因此相当于@Cacheable注解在这种情况是不生效的。

@Service
@Slf4j
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class CalDataInitServiceImpl implements ICalDataInitService {

    @Cacheable(key = "#root.methodName + #sendCardName")
    public int getSlotCount(String sendCardName) {
        ..方法体
        return calCard.getSlotCount();
    }
    
    ...
        
	@CachePut(key = "#param")
	public String updateCache(String param) {
        getSlotCount("xx");
    	// 对数据库更新某个值
  	   return updatedValue;
	}

    
}

标签:缓存,String,Spring,Cache,Caffeine,param,key,方法,public
From: https://www.cnblogs.com/scottyzh/p/17900921.html

相关文章

  • 【转载】Springboot2.x单元测试
    参考https://blog.csdn.net/wangxi06/article/details/114630426https://blog.csdn.net/qq_44381387/article/details/120869168(新版spring-boot-starter-test不再集成junit,而是junit-jupiter,无需@RunWith)https://www.jianshu.com/p/34f57f41af70https://www.cnblogs.co......
  • ASP.NET WebApi(.Net Framework) 应用CacheManager
    ASP.NETWebApi(.NetFramework)应用CacheManager,内存+Redis1,WebApi版本选.net4.6.2以上版本2,nuget包Unity(4.0.0.1)Unity.AspNet.WebApi(4.0.0.1)CacheManager.CoreCacheManager.Microsoft.Extensions.Caching.MemoryCacheManager.Microsoft.Extensions.ConfigurationCacheMa......
  • Spring状态机
    1.依赖<!--状态机--><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>2.0.1.RELEASE</version></dependency>2.状态枚举类pu......
  • SpringBoot接收日期参数异常
    一、关于接收前端传递的日期参数的问题:前提:Date类型的属性上添加了以下注解:@JsonFormat(timezone="GMT+8",pattern="yyyy-MM-dd")@DateTimeFormat(pattern="yyyy-MM-dd")@JSONField(format="yyyy-MM-dd")1、java.sql.date:空字符串解析报错,正常日期格式字符串没问......
  • SpringBoot中@Transactional失效场景
    一、背景:1、需求  定时器需要定时到“消息通知表”中获取“消息反馈表”中不存在的数据,遍历这些数据,并对每一条数据发起流程,不管发起成功与否都需要往消息反馈表中插入一条该数据的发起结果,若发起成功还需要往“核查案件表”中插入一条该案件的主表数据2、问题:  发现在发......
  • 国产化软件新浪潮: spring 改造替代...
    中午看了篇《国产化软件新浪潮:jdkredismysqltomcatnginx改造替代品及信创名录》想给它补充个spring改造替代:)七、Spring替代品-Solon生态1、Java新的生态级框架从零开始构建,有自己的标准规范与开放生态(历时五年,具备全球第二级别的生态规模)。与其他框架相比,解决了两......
  • springboot虚拟线程(jdk21,springboot3.2.0)
    1.什么是虚拟线程虚拟线程是JDK21版本正式发布的一个新特性。虚拟线程和平台线程主要区别在于,虚拟线程在运行周期内不依赖操作系统线程:它们与硬件脱钩,因此被称为“虚拟”。这种解耦是由JVM提供的抽象层赋予的。虚拟线程的运行成本远低于平台线程。它们消耗的内存要少得多。这就......
  • 【SpringBootWeb入门-12】MySQL-DDL-图形化工具
    1、章节前言上一篇文章我们讲解了MySQL的安装与配置,以及相关sql命令的执行操作,在演示这些sql语句的时候,我们都是在命令行当中进行操作的,在命令行当中敲写语句很不方便,主要原因有以下几点:无提示:命令行当中输入任何sql语句没有任何提示,全凭记忆,而且很容易敲错代码;操作繁琐:全部的......
  • SpringSecurity
    SpringSecurity1简介SpringSecurity是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。一般来说中大型的项目都是使用SpringSecurity来做安全框架。小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更......
  • springboot004旅游路线规划系统(Java毕业设计,附数据库和源码)
    第一章绪论1.1选题背景与研究意义随着社会的不断进步,在居民生活水平提高的同时,人们当前在生活的方方面面也越来越注重服务所带来的体验,随着近几年国家政策大力发展旅游业,旅游景点的建设越来也完善,旅游业的发展速度得到了显著的提升。各大旅行社、旅游景点都不断的推出新的活动计......