您现在的位置是:首页 > 正文

Spring Cache 集成 Caffeine实现项目缓存

2024-04-01 06:45:28阅读 4

此文章已同步更新至我的个人博客https://simonting.gitee.io


一、前言

Spring Cache本身是Spring框架中一个缓存体系的抽象实现,本身不具备缓存能力,需要配合具体的缓存实现来完成,如Ehcache、Caffeine、Guava、Redis等。

二、缓存注解

  • @EnableCaching:开启缓存功能
  • @Cacheable:定义缓存,用于触发缓存
  • @CachePut:定义更新缓存,触发缓存更新
  • @CacheEvict:定义清楚缓存,触发缓存清除
  • @Caching:组合定义多种缓存功能
  • @CacheConfig:定义公共设置,位于class之上

三、实战操作

我选择使用目前最受欢迎的Caffeine来作为具体的缓存实现方式,下面是一个demo:

1、依赖引入

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.8.6</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2、yaml配置

spring:
  cache:
    cache-names: USER
    caffeine:
      spec: initialCapacity=50,maximumSize=500,expireAfterWrite=5s
    type: caffeine

Caffeine配置说明

  • initialCapacity=[integer]: 初始的缓存空间大小
  • maximumSize=[long]: 缓存的最大条数
  • maximumWeight=[long]: 缓存的最大权重
  • expireAfterAccess=[duration]: 最后一次写入或访问后经过固定时间过期
  • expireAfterWrite=[duration]: 最后一次写入后经过固定时间过期
  • refreshAfterWrite=[duration]: 创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存
  • weakKeys: 打开key的弱引用
  • weakValues:打开value的弱引用
  • softValues:打开value的软引用
  • recordStats:开发统计功能

注意

  • expireAfterWrite和expireAfterAccess同事存在时,以expireAfterWrite为准。
  • maximumSize和maximumWeight不可以同时使用
  • weakValues和softValues不可以同时使用

3、开启缓存

4、模拟方法

service层

@Service
@Slf4j
public class CaffeineService {

    public static Map<String, String> map = new HashMap<>();

    static {
        map.put("1", "zhangsan");
        map.put("2", "lisi");
        map.put("3", "wangwu");
    }

    @Cacheable(value = "USER", key = "#id")
    public String getUser(String id) {
        log.info("getUser() run......");
        return map.get(id);
    }

    @CachePut(value = "USER", key = "#id")
    public String updateUser(String id, String name) {
        log.info("updateUser() run......");
        map.put(id, name);
        return map.toString();
    }

    @CacheEvict(value = "USER", key = "#id")
    public String delUser(String id) {
        log.info("delUser() run......");
        map.remove(id);
        return map.toString();
    }

}

controller层

@RestController
@RequestMapping("/cache")
@Slf4j
public class CaffeineController {

    @Autowired
    private CaffeineService caffeineService;

    @GetMapping("/user/{id}")
    public String getUser(@PathVariable String id) {
        long start = System.currentTimeMillis();
        String res = caffeineService.getUser(id);
        long end = System.currentTimeMillis();
        log.info("查询耗时:" + (end - start));
        return res;
    }

    @GetMapping("/user/{id}/{name}")
    public String updateUser(@PathVariable String id, @PathVariable String name) {
        return caffeineService.updateUser(id, name);
    }

    @DeleteMapping("/user/{id}")
    public String delUser(@PathVariable String id) {
        return caffeineService.delUser(id);
    }
}

5、测试

第一次查询:

第二次查询:

查询耗时明显小于第一次查询,因为第二次直接返回缓存,速度提升。

执行更新后再查询:
会使缓存失效。会重新执行查询方法查询

执行删除后再查询:
会使缓存失效。会重新执行查询方法查询

6、改造

上述通过yaml文件配置的方式不够灵活,无法实现多种缓存策略,所以现在一般使用javaconfig的形式进行配置。

下面是示例代码:

@Configuration
public class CaffeineConfig {

    @Bean
    public CacheManager caffeineCacheManager() {
        SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
        List<CaffeineCache> caffeineCaches = new ArrayList<>();
        for (CacheType cacheType : CacheType.values()) {
            caffeineCaches.add(new CaffeineCache(cacheType.name(),
                    Caffeine.newBuilder()
                            .expireAfterWrite(cacheType.getExpires(), TimeUnit.SECONDS)
                            .build()));
        }
        simpleCacheManager.setCaches(caffeineCaches);
        return simpleCacheManager;
    }
}
public enum CacheType {

    USER(5),
    TENANT(20);

    private int expires;

    CacheType(int expires) {
        this.expires = expires;
    }

    public int getExpires() {
        return expires;
    }

}

这样我们就能对USER设置5秒消防时间,对TENANT设置20秒消亡时间,在实际项目中这种方式更加的灵活。

网站文章

  • cookie

    将不同的计算机通过网络应用使用对应的传输介质进行传输物理层、数据链路层、网络层、传输层、会话层、表示层、应用层[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7sSptn...

    2024-04-01 06:45:21
  • 魔兽正式服5区服务器互通信息,《魔兽世界》一区合并服务器正式通告

    为了迎接1.12版本中的跨服务器战场功能,并进一步提升服务器表现以满足用户的需求,我们将对现有服务器的架构进行调整,一区至五区的服务器将进行小规模的服务器合并操作。我们已经从今天凌晨5点开始,对一区的...

    2024-04-01 06:45:14
  • 15HD_OJ——Tian Ji -- The Horse Racing

    15HD_OJ——Tian Ji -- The Horse Racing

    /* * Copyright (c) 2014, 烟台大学计算机学院 * All rights reserved. * 文件名称:test.cpp * 作 者:李晓凯 * 完成日期:2015年 6 月 11 日 * 版 本 号:v1.0 * * 问题描述: * 输入描述: * 程序输出: */ Problem Descriptio

    2024-04-01 06:44:35
  • react 表格高度自适应

    使用calc()给元素的border、margin、pading、font-size和width等属性设置动态值。不过calc()最大的好处就是用在流体布局上,可以通过calc()计算得到元素的宽度。...

    2024-04-01 06:44:19
  • Array.prototype.at()

    Array.prototype.at()

    看到MDN上Array有一个新的函数,但还在实验中,并没有正式开放 实际上在chrome浏览器上new一个数组,也是存在这个函数的。 at的含义就是快速定位数组中的第几项。比如我们不知道一个数组的长度...

    2024-04-01 06:43:40
  • 码农行业的术语,为啥又多,又难懂?

    什么是鲁棒。 什么是Predicate 什么协变,什么逆变 什么注入,什么反向控制 什么内聚,什么响应式 而且,猴子,竖子都能立山头,自己发明,创造新概念,新名词。 同一个概念,不同的英文,不同的解释,然后,重复的学习。 这个行业,什么时候能被一统江湖。 转载于:https://www.cnblogs.com/maoyan/p/10460556.html...

    2024-04-01 06:43:32
  • JAVA语言身份证校验位的计算

    用于身份证号校验位的计算import java.util.Scanner;public class ID_card {public static void main(String[] args) { System.out.println("请输入需验证的身份证号码的前17位:"); Scanner scan=new Scanner(System.in); int a=0; int r...

    2024-04-01 06:43:25
  • Toolbar中Menu不显示

    在Activity中使用如下代码来引入toolbar: @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.base_toolbar_menu,menu);

    2024-04-01 06:43:19
  • 生产企业原材料订购与运输的研究-数据处理课程设计

    生产企业原材料订购与运输的研究-数据处理课程设计

    生产企业原材料订购与运输的研究-数据处理课程设计

    2024-04-01 06:42:37
  • 字符串截取

    splice字符串截取,原数组不变 // 截取“周” 每周,6,09时45分59秒 let a = &quot;每周,6,09时45分59秒&quot; console.log(a.splice(1,...

    2024-04-01 06:42:30