本文正在参与「金石计划」
内存缓存Caffiene自界说CacheManager | 一灰灰Blog
上一篇介绍了Caffiene整合Spring的缓存注解@Cacheable,在这篇示例中,一切的缓存共用,但是实际的场景中,咱们或许会更期望针对不同的场景,装备不同的缓存(比如我的要害数据,虽然访问频率或许没那么高,但是每次实际读取的本钱很高,又不怎样变化,我期望能够更持久的缓存;不期望这些数据由于缓存的筛选战略被其他的热点数据给筛选掉),那么能够怎样处理呢?
接下来咱们来看一下两种不同的办法,来完成上面的诉求
项目装备
1. 依靠
首先搭建一个标准的SpringBoot项目工程,相关版别以及依靠如下
本项目借助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
进行开发
<dependencies>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
</dependencies>
与前面不同,咱们不需要在装备文件中指定缓存类型以及caffeine的相关条件参数,直接放在装备类中
2. 装备类
@Configuration
public class CacheConfig {
@Primary
@Bean("customCacheManager")
public CacheManager customCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
List<Cache> cacheList = new ArrayList<>();
cacheList.add(customCache());
simpleCacheManager.setCaches(cacheList);
return simpleCacheManager;
}
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.maximumSize(200)
.initialCapacity(100)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats()
.build(),
true);
}
}
留意上面的 cacheList,其间传入的就是Cache
目标,每个Cache目标就能够理解为一个缓存实例,要点留意构造参数中的第一个customCache
,这个就是后面缓存详细运用时,注解中的cacheNames
特点
运用实例
1. SimpleCacheManager 运用实例
@Service
@CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager")
public class AnoCacheService {
/**
* 用一个map来模拟存储
*/
private Map<Integer, User> userDb = new ConcurrentHashMap<>();
/**
* 增加数据,并保存到缓存中, 不论缓存中有没有,都会更新缓存
*
* @param user
*/
@CachePut(key = "#user.uid")
public User saveUser(User user) {
userDb.put(user.getUid(), user);
return user;
}
/**
* 优先从缓存中获取数据,若不存在,则从 userDb 中查询,并会将成果写入到缓存中
*
* @param userId
* @return
*/
@Cacheable(key = "#userId")
public User getUser(int userId) {
System.out.println("doGetUser from DB:" + userId);
return userDb.get(userId);
}
@CacheEvict(key = "#userId")
public void removeUser(int userId) {
userDb.remove(userId);
}
}
要点留意一下上面的@CacheConfig
,它界说了这个类中的的缓存,都运用 customCacheManager
缓存管理器,且详细的缓存为界说的customCache
(改成其他的会报错)
从上面的装备声明,也能够看出,当咱们期望运用多个缓存时,能够直接如下面这种办法进行扩展即可
@Primary
@Bean("customCacheManager")
public CacheManager customCacheManager() {
SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
List<Cache> cacheList = new ArrayList<>();
cacheList.add(customCache());
cacheList.add(customCache2());
simpleCacheManager.setCaches(cacheList);
return simpleCacheManager;
}
public Cache customCache() {
return new CaffeineCache("customCache", Caffeine.newBuilder()
.maximumSize(200)
.initialCapacity(100)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats()
.build(),
true);
}
public Cache customCache2() {
return new CaffeineCache("customCache2", Caffeine.newBuilder()
.maximumSize(100)
.initialCapacity(10)
.expireAfterWrite(30, TimeUnit.MINUTES)
.recordStats()
.build(),
true);
}
2. CaffeineCacheManager 办法
除了上面这种办法之外,咱们当然也能够再额定界说一个CacheManager,如下
@Bean("otherCacheManager")
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
// 设置过期时刻,写入后五分钟国企
.expireAfterWrite(5, TimeUnit.MINUTES)
// 初始化缓存空间大小
.initialCapacity(100)
// 最大的缓存条数
.maximumSize(200));
return cacheManager;
}
运用上面这种办法,cacheName能够不需要指定,详细运用如下
/**
* 1. cacheManager 指定详细的缓存管理器
* 2. cacheName 表明这个缓存前缀
* 3. 通过CacheConfig 注解进行修饰,表明适用于这个类下的一切公共办法
*
* @author YiHui
* @date 2023/3/5
*/
@Service
@CacheConfig(cacheNames = "ano2", cacheManager = "otherCacheManager")
public class AnoCacheService2 {
/**
* 用一个map来模拟存储
*/
private Map<Integer, User> userDb = new ConcurrentHashMap<>();
/**
* 增加数据,并保存到缓存中, 不论缓存中有没有,都会更新缓存
*
* @param user
*/
@CachePut(key = "#user.uid")
public User saveUser(User user) {
userDb.put(user.getUid(), user);
return user;
}
/**
* 优先从缓存中获取数据,若不存在,则从 userDb 中查询,并会将成果写入到缓存中
*
* @param userId
* @return
*/
@Cacheable(key = "#userId")
public User getUser(int userId) {
System.out.println("doGetUser from DB:" + userId);
return userDb.get(userId);
}
@CacheEvict(key = "#userId")
public void removeUser(int userId) {
userDb.remove(userId);
}
}
办法的内部完成完全一致;要点看@CacheConfig
中的特点值
- cacheNames 表明这个缓存前缀,没有束缚限制
3. 测验
上面介绍了两种运用不同缓存的姿态:
- SimpleCacheManager: 界说多个Cache
- 多个CacheManager
咱们写个简略的验证上面两个CacheManager表明不同缓存的测验用例
@RestController
public class TestController {
@Autowired
private AnoCacheService anoCacheService;
@Autowired
private AnoCacheService2 anoCacheService2;
private AtomicInteger uid = new AtomicInteger(1);
private AtomicInteger uid2 = new AtomicInteger(1);
@RequestMapping(path = "save")
public User save(String name,
@RequestParam(required = false, defaultValue = "1") Integer type) {
if (type == 1) {
return anoCacheService.saveUser(new User(uid.getAndAdd(1), name));
} else {
return anoCacheService2.saveUser(new User(uid2.getAndAdd(1), name));
}
}
@RequestMapping(path = "query")
public User query(int userId, @RequestParam(required = false, defaultValue = "1") Integer type) {
User user = type == 1 ? anoCacheService.getUser(userId) : anoCacheService2.getUser(userId);
return user == null ? new User() : user;
}
@RequestMapping(path = "remove")
public String remove(int userId, @RequestParam(required = false, defaultValue = "1") Integer type) {
if (type == 1) anoCacheService.removeUser(userId);
else anoCacheService2.removeUser(userId);
return "ok";
}
}
操作步骤:
- anoCacheService 写入缓存
- anoCacheService2 检查缓存,此时不应该能查到前面写入的缓存
不能错失的源码和相关知识点
0. 项目
- 工程:github.com/liuyueyi/sp…
- 源码:github.com/liuyueyi/sp…
系列博文:
- 【Spring专栏】内存缓存Caffeine根本运用姿态-技能派
- 【Spring专栏】内存缓存Caffeine整合Cachebale注解-技能派
- 【Spring专栏】内存缓存Caffiene自界说CacheManager-技能派
1. 微信公众号: 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不惜感激
下面一灰灰的个人博客,记载一切学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰Blog个人博客 blog.hhui.top
- 一灰灰Blog-Spring专题博客 spring.hhui.top