本文正在参与「金石计划」

内存缓存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 检查缓存,此时不应该能查到前面写入的缓存

SpringBoot系列教程之内存缓存Caffiene自定义CacheManager

不能错失的源码和相关知识点

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