Guava Cache 是一个全内存的本地缓存实现,而且提供了线程安全机制,相比于数据库或 Redis 存储,访问内存中的数据会更加高效。
Guava 官网介绍,下面的这几种情况可以考虑使用 Guava Cache:
愿意消耗一些内存空间来提升速度。
预料到某些键会被多次查询。
缓存中存放的数据总量不会超出内存容量。
所以,可以将频繁用到的少量数据存储到 Guava Cache 中,以提高程序性能。下面我们来看看 Guava Cache 具体用法。
添加依赖
在 pom.xml 中添加 spring-boot-starter-cache
和 guava
依赖。
1 | <dependency> |
2 | <groupId>org.springframework.boot</groupId> |
3 | <artifactId>spring-boot-starter-cache</artifactId> |
4 | </dependency> |
5 | |
6 | <!--guava 依赖--> |
7 | <dependency> |
8 | <groupId>com.google.guava</groupId> |
9 | <artifactId>guava</artifactId> |
10 | <version>27.0.1-jre</version> |
11 | </dependency> |
准备数据
模拟数据库的数据
1 | /** |
2 | * 数据工厂,模拟数据库的数据 |
3 | * |
4 | * @author star |
5 | **/ |
6 | public class DataFactory { |
7 | |
8 | private DataFactory() { |
9 | } |
10 | |
11 | private static List<UserDto> userDtoList; |
12 | |
13 | static { |
14 | // 初始化集合 |
15 | userDtoList = new ArrayList<>(); |
16 | |
17 | UserDto user = null; |
18 | for (int i = 0; i < 5; i++) { |
19 | user = new UserDto(); |
20 | user.setName("star" + i); |
21 | user.setAge(23); |
22 | userDtoList.add(user); |
23 | } |
24 | } |
25 | |
26 | public static List<UserDto> getUserDaoList() { |
27 | return userDtoList; |
28 | } |
29 | } |
创建 Guava Cache 配置类
Guava Cache 配置比较简洁,下面配置了缓存数据的过期时间是 10s,最大缓存容量是 1000 个。
1 |
|
2 |
|
3 | public class GuavaCacheConfig { |
4 | |
5 | /** |
6 | * 设置缓存管理器 |
7 | */ |
8 | |
9 | public CacheManager cacheManager(){ |
10 | GuavaCacheManager cacheManager = new GuavaCacheManager(); |
11 | |
12 | cacheManager.setCacheBuilder(CacheBuilder.newBuilder() |
13 | // 缓存过期时间 |
14 | .expireAfterWrite(10, TimeUnit.SECONDS) |
15 | // 缓存最大容量是 1000 |
16 | .maximumSize(1000) |
17 | ); |
18 | return cacheManager; |
19 | } |
20 | |
21 | } |
Guava Cache 除了代码中提到的设置缓存过期时间的策略外,还有其他的策略。下面是 Guava Cache 设置缓存过期时间的策略:
expireAfterAccess: 当缓存项在指定的时间段内没有被读或写就会被回收。
expireAfterWrite:当缓存项在指定的时间段内没有更新就会被回收,如果我们认为缓存数据在一段时间后数据不再可用,那么可以使用该种策略。
refreshAfterWrite:当缓存项上一次更新操作之后的多久会被刷新。
编写业务代码
- 编写 DAO 层
1 | /** |
2 | * UserRepository |
3 | * |
4 | * @author star |
5 | **/ |
6 |
|
7 | public class UserRepository { |
8 | |
9 | /** |
10 | * 获取用户信息(此处是模拟的数据) |
11 | */ |
12 | public UserDto getUserByName(String username) { |
13 | UserDto user = getUserFromList(username); |
14 | return user; |
15 | } |
16 | |
17 | /** |
18 | * 从模拟的数据集合中筛选 username 的数据 |
19 | */ |
20 | private UserDto getUserFromList(String username) { |
21 | |
22 | List<UserDto> userDaoList = DataFactory.getUserDaoList(); |
23 | for (UserDto user : userDaoList) { |
24 | if (Objects.equals(user.getName(), username)) { |
25 | return user; |
26 | } |
27 | } |
28 | return null; |
29 | } |
30 | } |
- 编写 Service 层
1 | /** |
2 | * UserService |
3 | * |
4 | * @author star |
5 | **/ |
6 |
|
7 | "guavaCache") // 声明缓存的名称 (cacheNames = |
8 | public class UserService { |
9 | |
10 | |
11 | private UserRepository userRepository; |
12 | |
13 | "#name") (key = |
14 | public UserDto getUserByName(String name) { |
15 | System.out.println("从数据库中获取数据,而不是读取缓存"); |
16 | return userRepository.getUserByName(name); |
17 | } |
18 | |
19 | } |
由于在上一篇 springboot-cache 已经对缓存用法做了详细说明,这里就简单介绍一下:
@Cacheable
: 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。@CachePut
:配置于方法上时,能够根据参数定义条件来进行缓存,其与 @Cacheable 不同的是,它不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中,所以主要用于数据新增和修改操作上。@CacheEvict
:配置于方法上时,表示从缓存中移除相应数据。编写 Controller 层
1 | /** |
2 | * UserResource |
3 | * |
4 | * @author star |
5 | **/ |
6 |
|
7 | "/api") ( |
8 | public class UserResource { |
9 | |
10 | |
11 | private UserService userService; |
12 | |
13 | |
14 | private CacheManager cacheManager; |
15 | |
16 | "/users/{name}") ( |
17 | public ResponseEntity<UserDto> getUser(@PathVariable String name) { |
18 | System.out.println("=================="); |
19 | UserDto user = userService.getUserByName(name); |
20 | System.out.println(cacheManager.toString()); |
21 | return ResponseEntity.ok(user); |
22 | } |
23 | } |
演示
通过多次向接口 http://localhost:8080/api/users/star1
GET 数据来观察效果:
可以看到缓存的启用和效果如下所示: