该项目是微服务缓存基础项目,框架现在还只支持添加缓存和失效缓存两种操作, 暂时还不能支持缓存更新(但其实失效后再添加就是更新了O(∩_∩)O~).
我们目标在将来提供
@CachePut注解, 以提供根据方法的入参/返回值进行缓存写入/更新, 详见#TODO列表
- 多级缓存设计&实现(调研中);
- 消除限制5:
@CachedPut注解(调研中); @Invalid开启前向清除缓存(调研中);- 缓存预热(调研中);
@Cached
Object func(@CacheKey("#arg0[#i]") List<Long> fIds, @CacheKey("#arg1[#i]") List<Long> aIds);
该模式会导致CacheX对方法参数做笛卡尔积, 结果将会计算产生大量的缓存
key, 性能损耗较大, 因此不支持.
@Cached
Object func(@CacheKey("#arg0[#i]") Map<Long, Object> map);
同上: 如果将Map内所有的
Key/Value进行交叉拼装为缓存key的话, 也会产生类似笛卡尔积的效果, 因此也不支持.
@Cached
Object func(@CacheKey("#arg0.keySet()[#i]") Map<Long, Object> map);
这种模式不常用且实现复杂、性能损耗较大, 因此不支持.
@Cached
Object func(@CacheKey("#arg0[#i]") List<Long> ids) {
return Collections.emptyList();
}
由于在批量模式下, Cache会在构造容器返回值时反射调用容器类的默认构造方法, 以及向容器内添加元素, 但这些容器并未暴露这些方法, 因此不能支持.
- 这类容器有:
- Arrays.ArrayList
- Collections.SingleList
- ...
- pom
<dependency>
<groupId>org.aoju.bus</groupId>
<artifactId>bus-cache</artifactId>
<version>${bus.version}</version>
</dependency>- XML注册
<!-- 启用自动代理: 如果已经开启则不必重复开启 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 配置CacheX切面(Cache代理需要手动生效) -->
<bean id="cache" class="CacheAspect">
<constructor-arg name="caches">
<map>
<entry key="default" value-ref="guava"/>
<entry key="redis" value-ref="redis"/>
</map>
</constructor-arg>
</bean>
org.aoju.bus.cache.support.cache.Cache实现 -->
<bean id="guava" class="GuavaCache">
<constructor-arg name="expire" value="600000"/>
<constructor-arg name="size" value="100000"/>
</bean>
<bean id="redis" class="RedisCache">
<constructor-arg name="jedisPool" ref="jedisPool"/>
</bean>- 在要添加缓存的方法上标
@Cached - 在要组装为key的方法参数上标
@CacheKey
CacheX提供如下注解
@Cached、@Invalid、@CacheKey. (ext:@CachedGet、@CachedWrite)
- 在需要走缓存的方法前添加
@Cached注解.
@Documented
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Cached {
/**
* @return Specifies the <b>Used cache implementation</b>,
* default the first {@code caches} config in {@code CacheXAspect}
*/
String value() default "";
/**
* @return Specifies the start prefix on every key,
* if the {@code Method} have non {@code param},
* {@code prefix}consts{@code Method}
*/
String prefix() default "";
/**
* @return use <b>SpEL</b>,
* when this spel is {@code true}, this {@code Method} will go through by cache
*/
String condition() default "";
/**
* @return expire time, time unit: <b>seconds</b>
*/
int expire() default Expire.FOREVER;
}| 属性 | 描述 | Ext |
|---|---|---|
value |
指定缓存实现: CacheXAspect/CacheXProxy的caches参数的key |
选填: 默认为注入caches的第一个实现(即caches的第一个Entry实例) |
prefix |
缓存key的统一前缀 | 选填: 默认为"", 若方法无参或没有@CacheKey注解, 则必须在此配置一个prefix, 令其成为缓存静态常量key |
condition |
SpEL表达式 | 选填: 默认为""(true), 在CacheX执行前会先eval该表达式, 当表达式值为true才会执行缓存逻辑 |
expire |
缓存过期时间(秒) | 选填: 默认为Expire.FOREVER |
- 在需要失效缓存的方法前添加
@Invalid注解.
@Documented
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Invalid {
/**
* @return as {@code @Cached}
* @since 0.3
*/
String value() default "";
/**
* @return as {@code @Cached}
* @since 0.3
*/
String prefix() default "";
/**
* @return as {@code @Cached}
* @since 0.3
*/
String condition() default "";
}注解内属性含义与
@Cached相同.
- 在需要作为缓存key的方法参数前添加
@CacheKey注解.
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheKey {
/**
* @return use a part of param as a cache key part
*/
String value() default "";
/**
* @return used multi model(value has `#i` index) and method return {@code Collection},
* the {@code field} indicate which of the {@code Collection}'s entity field related with this param
*/
String field() default "";
}| 属性 | 描述 | Ext |
|---|---|---|
value |
SpEL表达式: 缓存key的拼装逻辑 | 选填: 默认为"" |
field |
批量模式(value参数包含#i索引)且方法返回值为Collection时生效: 指明该返回值的某个属性是与该参数是关联起来的 |
详见Ext.批量模式 |
- 在需要走缓存的方法前添加
@CachedGet注解.
与
@Cached的不同在于@CachedGet只会从缓存内查询, 不会写入缓存(当缓存不存在时, 只是会取执行方法, 但不讲方法返回内容写入缓存).
@Documented
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CachedGet {
/**
* @return Specifies the <b>Used cache implementation</b>,
* default the first {@code caches} config in {@code CacheXAspect}
* @since 0.3
*/
String value() default "";
/**
* @return Specifies the start keyExp on every key,
* if the {@code Method} have non {@code param},
* {@code keyExp}consts{@code Method}
* @since 0.3
*/
String prefix() default "";
/**
* @return use <b>SpEL</b>,
* when this spel is {@code true}, this {@Code Method} will go through by cache
* @since 0.3
*/
String condition() default "";
}注解内属性含义与
@Cached相同.
在该模式下: #i指定了ids作为批量参数: 假设ids={1,2,3}, CacheX会结合前面的prefix组装出 {[USER]:1、[USER]:2、[USER]:3} 这3个key去批量的查询缓存, 假设只有{1,2}能够命中, 则CacheX会只保留{3}去调用getUsers()方法, 将返回值写入缓存后, 将两部分内容进行merge返回.
- 注意1: 如果方法的返回值为
Collection实例: 则@CacheKey必须指定field参数, 该参数会指定Collection元素(如User)内的某个属性(如id)与批量参数的元素(如ids内的元素项)是一一对应的, 这样CacheX就可以根据该属性提取出参数值, 拼装key然后写入缓存. - 注意2. 如果方法的返回值为
Map实例: 则field属性不填, 默认使用Map的Key作为field. - 注意3.
#i作为批量模式指示器, 批量模式需要使用#i来开启,#i指明某个参数作为批量参数, CacheX会不断的迭代该参数生成批量缓存key进行缓存的读写.
对于@CacheKey内的value属性(SpEL), CacheX在将方法的参数组装为key时, 会将整个方法的参数导入到SpEL的执行环境内,
所以在任一参数的@CacheKey的value属性内都可以自由的引用这些变量, 如:

arg0我们可以引用整个方法的任意参数, 但为了可读性, 我们仍然建议对某个参数的引用放在该参数自己的@CacheKey内
注意: 在Java8环境中, 如果编译时没有指定
-parameters参数, 则参数名默认为arg0、arg1、...、argN, 如果指定了该参数, 则在spel中使用实际的参数名即可, 如:#source.name(); 为了兼容这两种方式, CacheX提供了自己的命名方式args0、args1、...、argsN, 使用户可以不用区分是否开启编译参数.



