# rec-cache **Repository Path**: xiaoh1680/rec-cache ## Basic Information - **Project Name**: rec-cache - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2020-01-02 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 缓存抽象增强 spring 官方提供了标准的[缓存抽象](https://docs.spring.io/spring/docs/5.1.10.RELEASE/spring-framework-reference/integration.html#cache),它大致实现了[JSR-107](https://github.com/jsr107/jsr107spec)。 但是它对缓存需求支持的程度有限,尤其是不支持批量缓存操作。 现已找到的资源只有[CacheX](https://github.com/feiqing/cachex)实现了批量模式。 为何不直接采用: - 上一次更新是1年之前; - 依赖了一些不太合理的库,如 jbox 等,还有一些我们用不上的功能; - 难以及时响应需求变更,无法掌控开发进度; 因此,我们决定做自己的缓存抽象库。 ## 使用指南 ### 引入 如果你想自己配置各个 bean,可以使用如下依赖: ```xml com.vova.rec rec-cache-base 1.0.0 ``` 如果想开箱即用,可以使用如下依赖,以获得自动配置: ```xml com.vova.rec rec-cache-spring-boot-starter 1.0.0 ``` 但是无论那种,都需要引入 redis 支持: ```xml org.springframework.boot spring-boot-starter-data-redis ``` ### 基于注解的使用 0. 所有被设置缓存的方法的出参都需要实现序列化接口 `Serializable`,因为默认序列化方式是 [kryo](https://github.com/EsotericSoftware/kryo)。此外,为了保证反序列化的字段兼容性(比如缓存了a字段,现在又加上了b字段,反序列化就会失败),kryo 应该使用较高版本(5及以上),它提供了兼容性。 1. 对方法进行缓存,单条 ``` @Cached(value = "redis", prefix = "test-", expire = Expire.HALF_HOUR) public TestEntity read(@CacheKey int id) { log.info("---> method called!"); return testMapper.getTest(id); } ``` 上面的示例表示:对 read 方法进行缓存;指定缓存实现 为 redis;缓存 key 用入参 id 的值,结合前缀 `test-`;过期时间是半个小时。 当访问没有走缓存时,日志 "---> method called!" 将会被打印出来(这个仅用于测试缓存是否生效)。 缓存实现的指定:默认使用第一个(HashMap#entrySet()遍历的第一个),如果只配置了一个缓存实现,可以不指定。目前在 starter 中只配置了 redis 这一个缓存实现。若有其它,需要重写配置,并在使用处显式指定所用的缓存。 缓存实现都会托管到 `com.vova.rec.cache.manager.CacheManager#cachePool` 中,其键名就是配置名。 2. 对方法进行缓存,批量模式 ``` @Cached(prefix = "test-", expire = Expire.HALF_HOUR) public List readList(@CacheKey(value = "#idList[#i]", field = "id") List idList) { log.info("---> method called, idList={}", idList); return testMapper.getTestList(idList); } ``` 与单条模式的区别有两点: - 入参出参都是集合类; - CacheKey 需要指定 SpEL 表达式和 field,前者指定遍历key的方式,后者指定 ## 整体设计 ### 依赖的核心技术 这里使用了两种 IoC 容器:spring、guice。其中 spring 负责将需要暴露的 bean 交给最外层环境的容器,而 guice 负责自身不需要暴露的 bean 的管理工作,这其实也顺带做了环境的隔离。 所以,当源码中同时出现 `@Inject` 与 `@Autowired` 的时候不要惊讶,只需要知道这个 bean 是被谁管理的就可以分辨了。比如 `StringRedisTemplate`,这显然是 spring-boot 提供的,所以需要 `@Autowired`。 ## TODO LIST 1. 添加对全局的默认缓存 TTL 设置; 2. 实现对不同缓存实现的进一步配置。比如针对 redis,可以加入不同的 redisTemplate 支持,并加入选取决策配置; ``` 先对 redis 操作做一个抽象,将其注入到 ICache 实现类中。但是这里不需要很完整的抽象,因为缓存场景比较特殊,所以没必要支持众多复杂的数据结构,差不多只需要对 string 的 set、setExpire、mset、msetExpire、delete、get、mget 等操作进行抽象。然后通过继承的方式实现不同的 redis 策略(读写分离,分片等)。 ```