SpringBoot 使用 Caffeine 本地缓存

一、本地缓存介绍

. 二、缓存组件 Caffeine 介绍

. 1、Caffeine 性能

. 2、Caffeine 配置说明

. 3、软引用与弱引用

. 三、SpringBoot 集成 Caffeine 两种方式

. 四、SpringBoot 集成 Caffeine 方式一

. 1、Maven 引入相关依赖

. 2、配置缓存配置类

. 3、定义测试的实体对象

. 4、定义服务接口类和实现类

. 5、测试的 Controller 类

. 五、SpringBoot 集成 Caffeine 方式二

. 1、Maven 引入相关依赖

. 2、配置缓存配置类

. 3、定义测试的实体对象

. 4、定义服务接口类和实现类

. 5、测试的 Controller 类


环境配置:

  • JDK 版本:1.8

  • Caffeine 版本:2.8.0

  • SpringBoot 版本:2.2.2.RELEASE

参考地址:

  • Spring Boot缓存实战 Caffeine

  • Caffeine Cache-高性能Java本地缓存组件

  • 博文示例项目 Github 地址:https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-caffeine-cache-example

一、本地缓存介绍

缓存在日常开发中启动至关重要的作用,由于是存储在内存中,数据的读取速度是非常快的,能大量减少对数据库的访问,减少数据库的压力。

之前介绍过 Redis 这种 NoSql 作为缓存组件,它能够很好的作为分布式缓存组件提供多个服务间的缓存,但是 Redis 这种还是需要网络开销,增加时耗。本地缓存是直接从本地内存中读取,没有网络开销,例如秒杀系统或者数据量小的缓存等,比远程缓存更合适。

二、缓存组件 Caffeine 介绍

按 Caffeine Github 文档描述,Caffeine 是基于 JAVA 8 的高性能缓存库。并且在 spring5 (springboot 2.x) 后,spring 官方放弃了 Guava,而使用了性能更优秀的 Caffeine 作为默认缓存组件。

1、Caffeine 性能

可以通过下图观测到,在下面缓存组件中 Caffeine 性能是其中最好的。

2、Caffeine 配置说明

参数类型描述
initialCapacityinteger初始的缓存空间大小
maximumSizelong缓存的最大条数
maximumWeightlong缓存的最大权重
expireAfterAccessduration最后一次写入或访问后经过固定时间过期
refreshAfterWriteduration最后一次写入后经过固定时间过期
refreshAfterWriteduration创建缓存或者最近一次更新缓存后经过固定的时间间隔,刷新缓存
weakKeysboolean打开 key 的弱引用
weakValuesboolean打开 value 的弱引用
softValuesboolean打开 value 的软引用
recordStats-开发统计功能

注意:

  • weakValues 和 softValues 不可以同时使用。

  • maximumSize 和 maximumWeight 不可以同时使用。

  • expireAfterWrite 和 expireAfterAccess 同事存在时,以 expireAfterWrite 为准。

3、软引用与弱引用

  • 软引用: 如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。

  • 弱引用: 弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存

1// 软引用
2Caffeine.newBuilder().softValues().build();
3
4// 弱引用
5Caffeine.newBuilder().weakKeys().weakValues().build();

三、SpringBoot 集成 Caffeine 两种方式

SpringBoot 有俩种使用 Caffeine 作为缓存的方式:

  • 方式一: 直接引入 Caffeine 依赖,然后使用 Caffeine 方法实现缓存。

  • 方式二: 引入 Caffeine 和 Spring Cache 依赖,使用 SpringCache 注解方法实现缓存。

下面将介绍下,这俩中集成方式都是如何实现的。

四、SpringBoot 集成 Caffeine 方式一

1、Maven 引入相关依赖

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4    <modelVersion>4.0.0</modelVersion>
 5
 6    <parent>
 7        <groupId>org.springframework.boot</groupId>
 8        <artifactId>spring-boot-starter-parent</artifactId>
 9        <version>2.2.2.RELEASE</version>
10    </parent>
11
12    <groupId>mydlq.club</groupId>
13    <artifactId>springboot-caffeine-cache-example-1</artifactId>
14    <version>0.0.1</version>
15    <name>springboot-caffeine-cache-example-1</name>
16    <description>Demo project for Spring Boot Cache</description>
17
18    <properties>
19        <java.version>1.8</java.version>
20    </properties>
21
22    <dependencies>
23        <dependency>
24            <groupId>org.springframework.boot</groupId>
25            <artifactId>spring-boot-starter-web</artifactId>
26        </dependency>
27        <dependency>
28            <groupId>com.github.ben-manes.caffeine</groupId>
29            <artifactId>caffeine</artifactId>
30        </dependency>
31        <dependency>
32            <groupId>org.projectlombok</groupId>
33            <artifactId>lombok</artifactId>
34        </dependency>
35    </dependencies>
36
37    <build>
38        <plugins>
39            <plugin>
40                <groupId>org.springframework.boot</groupId>
41                <artifactId>spring-boot-maven-plugin</artifactId>
42            </plugin>
43        </plugins>
44    </build>
45
46</project>

2、配置缓存配置类

 1import com.github.benmanes.caffeine.cache.Cache;
 2import com.github.benmanes.caffeine.cache.Caffeine;
 3import org.springframework.context.annotation.Bean;
 4import org.springframework.context.annotation.Configuration;
 5import java.util.concurrent.TimeUnit;
 6
 7@Configuration
 8public class CacheConfig {
 9
10    @Bean
11    public Cache<String, Object> caffeineCache() {
12        return Caffeine.newBuilder()
13                // 设置最后一次写入或访问后经过固定时间过期
14                .expireAfterWrite(60, TimeUnit.SECONDS)
15                // 初始的缓存空间大小
16                .initialCapacity(100)
17                // 缓存的最大条数
18                .maximumSize(1000)
19                .build();
20    }
21
22}

3、定义测试的实体对象

 1import lombok.Data;
 2import lombok.ToString;
 3
 4@Data
 5@ToString
 6public class UserInfo {
 7    private Integer id;
 8    private String name;
 9    private String sex;
10    private Integer age;
11}

4、定义服务接口类和实现类

UserInfoService

 1import mydlq.club.example.entity.UserInfo;
 2
 3public interface UserInfoService {
 4
 5    /**
 6     * 增加用户信息
 7     *
 8     * @param userInfo 用户信息
 9     */
10    void addUserInfo(UserInfo userInfo);
11
12    /**
13     * 获取用户信息
14     *
15     * @param id 用户ID
16     * @return 用户信息
17     */
18    UserInfo getByName(Integer id);
19
20    /**
21     * 修改用户信息
22     *
23     * @param userInfo 用户信息
24     * @return 用户信息
25     */
26    UserInfo updateUserInfo(UserInfo userInfo);
27
28    /**
29     * 删除用户信息
30     *
31     * @param id 用户ID
32     */
33    void deleteById(Integer id);
34
35}

UserInfoServiceImpl

 1import com.github.benmanes.caffeine.cache.Cache;
 2import lombok.extern.slf4j.Slf4j;
 3import mydlq.club.example.entity.UserInfo;
 4import mydlq.club.example.service.UserInfoService;
 5import org.springframework.beans.factory.annotation.Autowired;
 6import org.springframework.stereotype.Service;
 7import org.springframework.util.StringUtils;
 8import java.util.HashMap;
 9
10@Slf4j
11@Service
12public class UserInfoServiceImpl implements UserInfoService {
13
14    /**
15     * 模拟数据库存储数据
16     */
17    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
18
19    @Autowired
20    Cache<String, Object> caffeineCache;
21
22    @Override
23    public void addUserInfo(UserInfo userInfo) {
24        log.info("create");
25        userInfoMap.put(userInfo.getId(), userInfo);
26        // 加入缓存
27        caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
28    }
29
30    @Override
31    public UserInfo getByName(Integer id) {
32        // 先从缓存读取
33        caffeineCache.getIfPresent(id);
34        UserInfo userInfo = (UserInfo) caffeineCache.asMap().get(String.valueOf(id));
35        if (userInfo != null){
36            return userInfo;
37        }
38        // 如果缓存中不存在,则从库中查找
39        log.info("get");
40        userInfo = userInfoMap.get(id);
41        // 如果用户信息不为空,则加入缓存
42        if (userInfo != null){
43            caffeineCache.put(String.valueOf(userInfo.getId()),userInfo);
44        }
45        return userInfo;
46    }
47
48    @Override
49    public UserInfo updateUserInfo(UserInfo userInfo) {
50        log.info("update");
51        if (!userInfoMap.containsKey(userInfo.getId())) {
52            return null;
53        }
54        // 取旧的值
55        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
56        // 替换内容
57        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
58            oldUserInfo.setAge(userInfo.getAge());
59        }
60        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
61            oldUserInfo.setName(userInfo.getName());
62        }
63        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
64            oldUserInfo.setSex(userInfo.getSex());
65        }
66        // 将新的对象存储,更新旧对象信息
67        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
68        // 替换缓存中的值
69        caffeineCache.put(String.valueOf(oldUserInfo.getId()),oldUserInfo);
70        return oldUserInfo;
71    }
72
73    @Override
74    public void deleteById(Integer id) {
75        log.info("delete");
76        userInfoMap.remove(id);
77        // 从缓存中删除
78        caffeineCache.asMap().remove(String.valueOf(id));
79    }
80
81}

5、测试的 Controller 类

 1import mydlq.club.example.entity.UserInfo;
 2import mydlq.club.example.service.UserInfoService;
 3import org.springframework.beans.factory.annotation.Autowired;
 4import org.springframework.web.bind.annotation.*;
 5
 6@RestController
 7@RequestMapping
 8public class UserInfoController {
 9
10    @Autowired
11    private UserInfoService userInfoService;
12
13    @GetMapping("/userInfo/{id}")
14    public Object getUserInfo(@PathVariable Integer id) {
15        UserInfo userInfo = userInfoService.getByName(id);
16        if (userInfo == null) {
17            return "没有该用户";
18        }
19        return userInfo;
20    }
21
22    @PostMapping("/userInfo")
23    public Object createUserInfo(@RequestBody UserInfo userInfo) {
24        userInfoService.addUserInfo(userInfo);
25        return "SUCCESS";
26    }
27
28    @PutMapping("/userInfo")
29    public Object updateUserInfo(@RequestBody UserInfo userInfo) {
30        UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);
31        if (newUserInfo == null){
32            return "不存在该用户";
33        }
34        return newUserInfo;
35    }
36
37    @DeleteMapping("/userInfo/{id}")
38    public Object deleteUserInfo(@PathVariable Integer id) {
39        userInfoService.deleteById(id);
40        return "SUCCESS";
41    }
42
43}

五、SpringBoot 集成 Caffeine 方式二

1、Maven 引入相关依赖

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4    <modelVersion>4.0.0</modelVersion>
 5
 6    <parent>
 7        <groupId>org.springframework.boot</groupId>
 8        <artifactId>spring-boot-starter-parent</artifactId>
 9        <version>2.2.2.RELEASE</version>
10    </parent>
11
12    <groupId>mydlq.club</groupId>
13    <artifactId>springboot-caffeine-cache-example-2</artifactId>
14    <version>0.0.1</version>
15    <name>springboot-caffeine-cache-example-2</name>
16    <description>Demo project for Spring Boot caffeine</description>
17
18    <properties>
19        <java.version>1.8</java.version>
20    </properties>
21
22    <dependencies>
23        <dependency>
24            <groupId>org.springframework.boot</groupId>
25            <artifactId>spring-boot-starter-web</artifactId>
26        </dependency>
27        <dependency>
28            <groupId>org.springframework.boot</groupId>
29            <artifactId>spring-boot-starter-cache</artifactId>
30        </dependency>
31        <dependency>
32            <groupId>com.github.ben-manes.caffeine</groupId>
33            <artifactId>caffeine</artifactId>
34        </dependency>
35        <dependency>
36            <groupId>org.projectlombok</groupId>
37            <artifactId>lombok</artifactId>
38        </dependency>
39    </dependencies>
40
41    <build>
42        <plugins>
43            <plugin>
44                <groupId>org.springframework.boot</groupId>
45                <artifactId>spring-boot-maven-plugin</artifactId>
46            </plugin>
47        </plugins>
48    </build>
49
50</project>

2、配置缓存配置类

 1@Configuration
 2public class CacheConfig {
 3
 4    /**
 5     * 配置缓存管理器
 6     *
 7     * @return 缓存管理器
 8     */
 9    @Bean("caffeineCacheManager")
10    public CacheManager cacheManager() {
11        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
12        cacheManager.setCaffeine(Caffeine.newBuilder()
13                // 设置最后一次写入或访问后经过固定时间过期
14                .expireAfterAccess(60, TimeUnit.SECONDS)
15                // 初始的缓存空间大小
16                .initialCapacity(100)
17                // 缓存的最大条数
18                .maximumSize(1000));
19        return cacheManager;
20    }
21
22}

3、定义测试的实体对象

1@Data
2@ToString
3public class UserInfo {
4    private Integer id;
5    private String name;
6    private String sex;
7    private Integer age;
8}

4、定义服务接口类和实现类

服务接口

 1import mydlq.club.example.entity.UserInfo;
 2
 3public interface UserInfoService {
 4
 5    /**
 6     * 增加用户信息
 7     *
 8     * @param userInfo 用户信息
 9     */
10    void addUserInfo(UserInfo userInfo);
11
12    /**
13     * 获取用户信息
14     *
15     * @param id 用户ID
16     * @return 用户信息
17     */
18    UserInfo getByName(Integer id);
19
20    /**
21     * 修改用户信息
22     *
23     * @param userInfo 用户信息
24     * @return 用户信息
25     */
26    UserInfo updateUserInfo(UserInfo userInfo);
27
28    /**
29     * 删除用户信息
30     *
31     * @param id 用户ID
32     */
33    void deleteById(Integer id);
34
35}

服务实现类

 1import lombok.extern.slf4j.Slf4j;
 2import mydlq.club.example.entity.UserInfo;
 3import mydlq.club.example.service.UserInfoService;
 4import org.springframework.cache.annotation.CacheConfig;
 5import org.springframework.cache.annotation.CacheEvict;
 6import org.springframework.cache.annotation.CachePut;
 7import org.springframework.cache.annotation.Cacheable;
 8import org.springframework.stereotype.Service;
 9import org.springframework.util.StringUtils;
10import java.util.HashMap;
11
12@Slf4j
13@Service
14@CacheConfig(cacheNames = "caffeineCacheManager")
15public class UserInfoServiceImpl implements UserInfoService {
16
17    /**
18     * 模拟数据库存储数据
19     */
20    private HashMap<Integer, UserInfo> userInfoMap = new HashMap<>();
21
22    @Override
23    @CachePut(key = "#userInfo.id")
24    public void addUserInfo(UserInfo userInfo) {
25        log.info("create");
26        userInfoMap.put(userInfo.getId(), userInfo);
27    }
28
29    @Override
30    @Cacheable(key = "#id")
31    public UserInfo getByName(Integer id) {
32        log.info("get");
33        return userInfoMap.get(id);
34    }
35
36    @Override
37    @CachePut(key = "#userInfo.id")
38    public UserInfo updateUserInfo(UserInfo userInfo) {
39        log.info("update");
40        if (!userInfoMap.containsKey(userInfo.getId())) {
41            return null;
42        }
43        // 取旧的值
44        UserInfo oldUserInfo = userInfoMap.get(userInfo.getId());
45        // 替换内容
46        if (!StringUtils.isEmpty(oldUserInfo.getAge())) {
47            oldUserInfo.setAge(userInfo.getAge());
48        }
49        if (!StringUtils.isEmpty(oldUserInfo.getName())) {
50            oldUserInfo.setName(userInfo.getName());
51        }
52        if (!StringUtils.isEmpty(oldUserInfo.getSex())) {
53            oldUserInfo.setSex(userInfo.getSex());
54        }
55        // 将新的对象存储,更新旧对象信息
56        userInfoMap.put(oldUserInfo.getId(), oldUserInfo);
57        // 返回新对象信息
58        return oldUserInfo;
59    }
60
61    @Override
62    @CacheEvict(key = "#id")
63    public void deleteById(Integer id) {
64        log.info("delete");
65        userInfoMap.remove(id);
66    }
67
68}

5、测试的 Controller 类

 1import mydlq.club.example.entity.UserInfo;
 2import mydlq.club.example.service.UserInfoService;
 3import org.springframework.beans.factory.annotation.Autowired;
 4import org.springframework.web.bind.annotation.*;
 5
 6@RestController
 7@RequestMapping
 8public class UserInfoController {
 9
10    @Autowired
11    private UserInfoService userInfoService;
12
13    @GetMapping("/userInfo/{id}")
14    public Object getUserInfo(@PathVariable Integer id) {
15        UserInfo userInfo = userInfoService.getByName(id);
16        if (userInfo == null) {
17            return "没有该用户";
18        }
19        return userInfo;
20    }
21
22    @PostMapping("/userInfo")
23    public Object createUserInfo(@RequestBody UserInfo userInfo) {
24        userInfoService.addUserInfo(userInfo);
25        return "SUCCESS";
26    }
27
28    @PutMapping("/userInfo")
29    public Object updateUserInfo(@RequestBody UserInfo userInfo) {
30        UserInfo newUserInfo = userInfoService.updateUserInfo(userInfo);
31        if (newUserInfo == null){
32            return "不存在该用户";
33        }
34        return newUserInfo;
35    }
36
37    @DeleteMapping("/userInfo/{id}")
38    public Object deleteUserInfo(@PathVariable Integer id) {
39        userInfoService.deleteById(id);
40        return "SUCCESS";
41    }
42
43}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值