Spring Boot 集成 Guava Cache 实现缓存机制

Guava Cache 是一个全内存的本地缓存实现,而且提供了线程安全机制,相比于数据库或 Redis 存储,访问内存中的数据会更加高效。

Guava 官网介绍,下面的这几种情况可以考虑使用 Guava Cache:

  • 愿意消耗一些内存空间来提升速度。

  • 预料到某些键会被多次查询。

  • 缓存中存放的数据总量不会超出内存容量。

所以,可以将频繁用到的少量数据存储到 Guava Cache 中,以提高程序性能。下面我们来看看 Guava Cache 具体用法。

添加依赖

在 pom.xml 中添加 spring-boot-starter-cacheguava 依赖。

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
@Configuration
2
@EnableCaching
3
public class GuavaCacheConfig {
4
5
    /**
6
     * 设置缓存管理器
7
     */
8
    @Bean
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
@Repository
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
@Service
7
@CacheConfig(cacheNames = "guavaCache") // 声明缓存的名称
8
public class UserService {
9
10
    @Autowired
11
    private UserRepository userRepository;
12
13
    @Cacheable(key = "#name")
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
@RestController
7
@RequestMapping("/api")
8
public class UserResource {
9
10
    @Autowired
11
    private UserService userService;
12
13
    @Autowired
14
    private CacheManager cacheManager;
15
16
    @GetMapping("/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 数据来观察效果:

get

可以看到缓存的启用和效果如下所示:

result

源码地址

springboot-guava-cache