上一篇介绍了Caffiene整合Spring的缓存注解@Cacheable,在这篇示例中,所有的缓存公用,但是实际的场景中,我们可能会更希望针对不同的场景,配置不同的缓存(比如我的关键数据,虽然访问频率可能没那么高,但是每次实际读取的成本很高,又不怎么变动,我希望可以更长久的缓存;不希望这些数据因为缓存的淘汰策略被其他的热点数据给淘汰掉),那么可以怎么处理呢?
接下来我们来看一下两种不同的方式,来实现上面的诉求
项目配置
1. 依赖
首先搭建一个标准的SpringBoot项目工程,相关版本以及依赖如下
本项目借助SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
进行开发
1 2 3 4 5 6 7 8 9 10 11
| <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. 配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @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 使用实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Service @CacheConfig(cacheNames = "customCache", cacheManager = "customCacheManager") public class AnoCacheService {
private Map<Integer, User> userDb = new ConcurrentHashMap<>();
@CachePut(key = "#user.uid") public User saveUser(User user) { userDb.put(user.getUid(), user); return user; }
@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
(改成其他的会报错)
从上面的配置声明,也可以看出,当我们希望使用多个缓存时,可以直接如下面这种方式进行扩展即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @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,如下
1 2 3 4 5 6 7 8 9 10 11 12
| @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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
@Service @CacheConfig(cacheNames = "ano2", cacheManager = "otherCacheManager") public class AnoCacheService2 {
private Map<Integer, User> userDb = new ConcurrentHashMap<>();
@CachePut(key = "#user.uid") public User saveUser(User user) { userDb.put(user.getUid(), user); return user; }
@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表示不同缓存的测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| @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. 项目
1. 微信公众号: 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
一灰灰blog
Be the first person to leave a comment!