说明:Caffeine是本地缓存方案,在所有本地缓存中命中率最佳,参考下图(引自http://t.csdn.cn/oiQlH),本文介绍Caffeine在SpringBoot项目中的应用。
使用
例如现在有两个接口,一个查询所有用户,一个根据ID查询用户,数据访问层使用的是Mybatis-plus;
controller层代码如下:
import com.hzy.pojo.User;
import com.hzy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("list")
public List<User> getUsers() {
return userService.list();
}
@GetMapping("{id}")
public User getUserById(@PathVariable Long id) {
return userService.getById(id);
}
}
第一步:引入依赖
使用Caffeine前先引入相关依赖:
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
第二步:创建Bean对象
针对需要用本地缓存的接口,创建对应的缓存对象,注意导包是"com.github……"里面的;
(创建一个配置类)
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.hzy.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CaffeineConfig {
/**
* 创建User相关接口的Cache对象,缓存数量上限为100,初始容量为10
* @return
*/
@Bean
public Cache<Long, User> userCache(){
return Caffeine.newBuilder()
.initialCapacity(10)
.maximumSize(100)
.build();
}
}
顺便一提,Caffeine提供了三种缓存驱逐策略,分别如下:
- 基于容量,设置缓存数量的上限;
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100) // 设置缓存数量上限
.build();
- 基于时间,设置缓存的有效时间;
Cache<String, String> cache = Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(10)) // 设置缓存有效期为最后一次写入后的10秒
.build();
- 基于引用,设置缓存为软件引用或者弱引用,利用JVM虚拟机的GC来回收缓存,性能差,不推荐;
第三步:修改代码
可以针对这两个接口,在Service层输入信息,来判断缓存是否生效,有没有每次都访问数据库;
(controller层,方法改为访问Service的方法,查询所有的接口不使用缓存)
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private Cache<Long, User> userCache;
/**
* 不使用缓存
* @return
*/
@GetMapping("list")
public List<User> getUsers() {
return userService.list();
}
/**
* 使用缓存
* @param id
* @return
*/
@GetMapping("{id}")
public User getUserById(@PathVariable Long id) {
return userCache.get(id, new Function<Long, User>() {
@Override
public User apply(Long key) {
return userService.getUserById(key);
}
});
}
}
(Service层代码,每次访问数据库前打印信息)
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
public User getUserById(Long id) {
System.out.println("根据ID查询用户接口====访问了数据库");
return getById(id);
}
}
第四步:测试使用
启动项目,使用Postman访问根据ID查询用户的接口,查看控制台结果;
(首次访问,控制台打印信息,请求访问了数据库)
清空控制台,二次访问,未打印信息,说明这次没有访问数据库,缓存生效了。此时即使停用数据库,这条请求依旧可以访问到结果
注意事项
使用Caffeine可以减轻数据库压力,但是需要注意,因为Caffeine是本地缓存,重启项目后发送请求还是直接访问数据库的。
另外,Caffeine好像不能使用Mybatis-Plus提供的,list()、selectList()这种全查的API,就是说只能在单条记录的查询下使用Caffeine,比较受限。
即便可以对list()、selectList()查询结果缓存,那Caffeine有没有对缓存进行管理呢?比如全查了所有用户信息,保留了缓存。此时首次查询一条用户信息,Caffeine有没有相关判断,直接从缓存中获取此次查询结果,而不访问数据库呢?值得思考。