Skip to content

redisCli annotation 文档

翡青 edited this page Feb 20, 2017 · 3 revisions

Maven

<dependency>
  <groupId>com.vdian.redis</groupId>
  <artifactId>redisCli-annotation</artifactId>
  <version>1.0.0-SNAPSHOT</version>
</dependency>

入门

  • 配置:

在Spring配置文件中加入如下两个配置:

<!-- 开启AOP自动代理 -->
<aop:aspectj-autoproxy/>
<!-- 加载com.vdian.redis.annotation.core.CacheManageAspect -->
<context:component-scan base-package="com.vdian.redis.annotation.core"/>
  • 加入缓存

在打算使用缓存的方法前添加@Cached注解, 并在想要组装成Key的参数前添加@CacheKey注解:

@Cached(namespace = "feedcenter", expireTime = CacheExpireTime.ONE_HOUR)
public AuthorFeed getAuthorFeed(@CacheKey(prefix = "feedId:") Long feedId) {
   // ...
}

添加如上注解后CacheManageAspect就会拼装出feedId:[feedId-Value]作为Key去查询Redis, 如果没有命中则回头执行getAuthorFeed()方法, 得到返回值AuthorFeed之后写入缓存, 并返回.

  • 失效缓存

在需要失效缓存的方法前添加@CacheInvalid注解, 并在想要组装成Key的参数前添加@CacheKey注解:

@CacheInvalid(namespace = "feedcenter")
public int deleteFeed(@CacheKey(prefix = "feedId:") long feedId, String authorId) {
   // ...
}

添加如上注解后CacheManageAspect就会拼装出feedId:[feedId-Value]作为Key去删除Redis中的缓存(虽然deleteFeed()方法还有一个authorId参数, 但他并未被@CacheKey标注,因此他并不会成为Key的一部分


Annotation详解

redisCli-annotation包一共提供三个注解@Cached@CacheInvalid@CacheKey.


@Cached

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cached {

    /**
     * @return cache命名空间
     */
    String namespace();

    /**
     * @return cache过期时间. Unit: second
     */
    int expireTime() default CacheExpireTime.FOREVER;

    /**
     * @return 多部分key之间的分隔符(如 part1:part2:part3)
     */
    String keySeparator() default ":";
}
属性 描述
namespace 填写在http://redis.vdian.net/ (日常: http://redis.daily.vdian.net/) 申请得到的namespace (必填)
expireTime 缓存过期时间, 单位为. 在CacheExpireTime接口中定义了一些推荐值, 也可自定义 (选填, 默认永不过期)
keySeparator 如果一个Key由多部分构成, 可由keySeparator对其进行连接, 详见keySeparator示例 (选填: 默认为":")
  • keySeparator示例
@Cached(namespace = "feedcenter", keySeparator="-")
int deleteFeed(@CacheKey(prefix = "feedId:") long feedId, @CacheKey(prefix = "authorId:")String authorId) {
	// ...
}

则最后拼装起来的key为feedId:[feedId-Value]-authorId:[authorId-Value], Key的两个部分以-连接.


@CacheInvalid

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheInvalid {

    /**
     * @return cache命名空间
     */
    String namespace();

    /**
     * @return 多部分key之间的分隔符(如 part1:part2:part3)
     */
    String keySeparator() default ":";
}

namespacekeySeparator含义与@Cached相同.


@CacheKey

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheKey {

    /**
     * @return key前缀(如 参数值为123, 前缀prefix为id, 则本部分key拼装起来就可以是: id123)
     */
    String prefix() default "";

    /**
     * @return 是否是一个批量key, 当要组装的Key参数为Collection(及其子类), 返回值为Collection/Map(及其子类)时需要用到
     */
    boolean batchKey() default false;

    /**
     * @return 当batchKey为true, 且返回值为Collection(及其子类)时使用, 以说明Result对象的哪一个属性与key对应
     */
    String resultExpression() default "";

    /**
     * @return 当想要使用一个对象的某个属性(如User的id属性)作为key时使用(如: keyExpression="id")
     */
    String keyExpression() default "";
}

@CacheKeyredisCli-annotation包的核心, 封装了缓存Key的拼装/解析规则, @Cached以及@CacheInvalid注解均需配合@CacheKey才能生效.

属性 描述
prefix 指定Key拼装前缀, 详见prefix示例
batchKey 标明该Key是否为一个批量Key, 详见batchKey示例
resultExpression batchKeytrue且方法返回Collection(及其子类)时使用, 详见batchKeyresultExpression示例
keyExpression 如果方法形参为Object, 但并不希望将整个Object作为Key时使用, 详见keyExpression示例

  • prefix示例:
@Cached(namespace = "feedcenter")
int deleteFeed(@CacheKey(prefix = "feedId:") long feedId, String authorId) {
	// ...
}

由于指定了prefix, 则最后拼装Key为feedId:[feedId-Value], 否则裸Key应该为[feedId-Value]. 使用prefix可防止当namespace相同时的Key冲突.


  • batchKeyresultExpression示例:
@Cached(namespace = "feedcenter")
public List<FeedUser> getFeedUsers(@CacheKey(prefix = "userId:", batchKey = true, resultExpression = "id") List<Long> feedIds) {
   // ...
}

batchKey标定该Key为一个批量Key, 比如以(1,2,3,4,5)List为参数调用getFeedUsers()方法, CacheManageAspect就会拼装出:

userId:1
userId:2
userId:3
userId:4
userId:5

一批Key去查询缓存, 如果其中只有userId:1userId:2userId:4命中缓存, CacheManageAspect则会继续以(3, 5)List去继续调用getFeedUsers()方法, 得到返回结果写入缓存. 最后将缓存查询结果与方法执行结果合并返回.

由于@CacheKey中标注了resultExpression = "id", 因此我们可以将返回值FeedUser中的id属性与Key中的Id值对一一对应起来. 比如以(1,2,3,4,5)参数传入, 我们也只会返回id为(1,2,3,4,5)的FeedUser对象组成的List.


  • batchKey与无resultExpression示例:
@Cached(namespace = "feedcenter")
public Map<Long, FeedUser> getFeedUsers(@CacheKey(batchKey = true) List<Long> feedIds) {
   // ...
}

虽然@CacheKey中指定了batchKey, 但并未指定resultExpression, 这是因为方法的返回值为Map, 我们默认Map的key直接对应参数值:

Map<Long, FeedUser> = {
	id=1 -> id=1的FeedUser对象;
	id=2 -> id=2的FeedUser对象;
	...
}

  • keyExpression示例:
@CacheInvalid(namespace = "feedcenter")
public boolean unLike(@CacheKey(keyExpression = "feedId") FeedLike feedLike) {
	// ...
}

虽然@CacheKey标记的是一个FeedLike对象, 但有时我们并不想将整个对象作为Key, 我们希望用他的一个属性如feedId作为Key, 因此就可以指定keyExpression = "feedId".


限制(不支持的功能)

1. batchKey时, 入参id列表与返回对象列表不能一一对应:

@Cached(namespace = "feedcenter")
public List<FeedUser> getFeedUsers(@CacheKey(prefix = "userId:", batchKey = true, resultExpression = "id") List<Long> feedIds) {
   // ...
}

比如以(1,2,3)List调用getFeedUsers()方法, 却希望返回{FeedUser(id=1), FeedUser(id=2), FeedUser(id=3), FeedUser(id=4)}List的对象列表, 多了一个id为4的FeedUser对象, 这种情况我们无法做到.


2. 没有@CacheKey注解

@Cached(namespace = "feedcenter")/@CacheInvalid(namespace = "feedcenter")
public List<FeedUser> getFeedUsers(List<Long> feedIds) {
   // ...
}

这样使用会直接抛出异常, Redis缓存不能没有Key.


3. 多batchKey

@Cached(namespace = "feedcenter")
public ReturnType getFeedUsers(@CacheKey(batchKey = true) List<Long> feedIds, @CacheKey(batchKey = true) List<Long> authorIds ) {
   // ...
}

如果Key是多部分Key(有多个@CacheKey注解), 且有多个批量属性(包含多个batchKey = true属性), 则在第一次运行时就会抛出异常.


4. 以Map作为batchKey

@Cached(namespace = "feedcenter")
public ReturnType getFeedUsers(@CacheKey(batchKey = true) Map<Long, FeedUser> map) {
   // ...
}

这种希望以Map的KeySet拼装Key列表的用法还不支持, 我们争取在下一期加入.