# 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 策略(读写分离,分片等)。
```