spring的缓存抽象
详情参看:《SpringBoot基础系列-SpringCache使用》
支持
ConcurrentMap、EhCache、Caffeine、JCache
接口
org.springframework.cache.Cache
org.springframework.cache.CacheManager
那些缓存的注解
@EnableCaching 开启缓存功能
- @Cacheable 定义缓存,用于触发缓存
- @CacheEvict 定义清除缓存,触发缓存清除
- @CachePut 定义更新缓存,触发缓存更新
- @Caching 组合定义多种缓存功能
- @CacheConfig 定义公共设置,位于class之上,如缓存名字
@EnableCaching
用于开启基于注解的缓存功能
@EnableCaching的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
/**
* 用于设置使用哪种代理方式,默认是基于接口的jdk动态代理(false)
* 设置为true,则使用基于继承的cglib动态代理
*/
boolean proxyTargetClass() default false;
/**
* 设置切面织入方式(设置面向切面编程的实现方式)
* 默认使用动态代理的方式织入,当然也可以设置为AspectJ的方式来实现aop
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* 设置在一个切入点存在多个通知的时候各个通知的执行顺序,默认为最低优先级
* 其中数字越大优先级越低,这里默认为最低优先级,Integer.MAX_VALUE整数最大值
* <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
/**上面选择的枚举值*/
public enum AdviceMode {
PROXY,
ASPECTJ
}
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
示列:
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cacheManager;
}
}
注意:在springboot中使用springCache可以由自动配置功能来完成CacheManager的注册,自动根据拥有的缓存系统注册对应的缓存管理器,也可以手动指定
@EnableCaching 等同于下面的xml配置
<beans>
<cache:annotation-driven/>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager>
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean>
<property name="name" value="default"/>
</bean>
</set>
</property>
</bean>
</beans>
@Cacheable
标注在方法上,方法返回结果被缓存起来
标注在类上,类里所有方法返回结果都被缓存起来
涉及的方法每次被调用前都会触发缓存校验,校验指定参数的缓存是否已存在,存在,直接返回缓存结果;否则执行方式,方法执行结果保存到缓存中。
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
/**指定缓存名称,与cacheNames()方法效果一样*/
@AliasFor("cacheNames")
String[] value() default {};
/** 指定缓存名称,与vale()方法效果一样*/
@AliasFor("value")
String[] cacheNames() default {};
/**
* 使用SpEL(Spring Expression Language)表达式手动指定缓存键的组合方式,默认情况使用所有的参数来组合成键,除非自定义了keyGenerator
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String key() default "";
/**
* 自定义键生成器,定义了该方法后,上面的key方法自动失效,这个键生成器是:
* org.springframework.cache.interceptor.KeyGenerator,函数式接口
* 只有一个generate方法,我们通过自定义的逻辑来实现自定义的key生成策略
*/
String keyGenerator() default "";
/**
* 设置自定义的CacheManager(缓存管理器),自动生成一个CacheResolver(缓存解析器)
* 与cacheResolver()方法设置互斥
*/
String cacheManager() default "";
/** 设置一个自定义的缓存解析器 */
String cacheResolver() default "";
/**
* 设置执行缓存的条件,如果条件不满足,方法返回的结果就不会被缓存,默认无条件全部缓存
* 使用SpEL(Spring Expression Language)来定义条件
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String condition() default "";
/**
* 用于禁用缓存功能,如果设置的条件满足,就不缓存结果
* 与上面condition()方法不同之处在于,该方法执行在当前方法调用结束,结果出来之后,
* 因此它除了可以使用上面condition所能使用的SpEl表达式外
* 还可以使用#result来获取方法的执行结果,根据结果的不同来决定是否缓存
*/
String unless() default "";
/**
* 是否对多个针对同一key执行缓存加载操作的线程进行同步,默认不同步
* 这个功能需要明确确定所使用的缓存工具是否支持该功能,否则不要滥用
*/
boolean sync() default false;
}
示列:
@Service
@Log4j2
public class AnimalService {
@Autowired
private AnimalRepository animalRepository;
//@Cacheable("animalById")
//缓存名称是animalByid,缓存的key是id的值
@Cacheable(value = "animalById", key = "#id")
public ResponseEntity<Animal> getAnimalById(final int id){
return ResponseEntity.ok(animalRepository.selectById(id));
}
}
注意:一般缓存名称必须要有,因为这个是区别于其他方法的缓存的唯一方法;
key = “#id” 指定当前方法缓存保存时的键组成,默认情况下使用所有的参数组合而成,这样可以有效区分不同参数的缓存。
也可以使用SpEl表达式手动指定
自定义一个keyGenerator
public class AnimalKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder("animal-");
sb.append(target.getClass().getSimpleName()).append("-").append(method.getName()).append("-");
for (Object o : params) {
String s = o.toString();
sb.append(s).append("-");
}
return sb.deleteCharAt(sb.lastIndexOf("-")).toString();
}
}
缓存结构
每个缓存名称下存在多个缓存条目
缓存条目对应不同参数调用方法时生成的缓存
所以一个缓存名称并不是一个缓存,而是一系列缓存
缓存名称
- 缓存条目(key:value)
- 缓存条目(key:value)
@CachePut
更新缓存,无论结果是否已经缓存,都会在方法执行结束后插入缓存,相当于更新缓存。一般用在更新方法上
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
/**指定缓存名称,与cacheNames()方法效果一样*/
@AliasFor("cacheNames")
String[] value() default {};
/** 指定缓存名称,与vale()方法效果一样*/
@AliasFor("value")
String[] cacheNames() default {};
/**
* 使用SpEL(Spring Expression Language)表达式手动指定缓存键的组合方式,默认情况使用所有的参数来组合成键,除非自定义了keyGenerator
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String key() default "";
/**
* 自定义键生成器,定义了该方法后,上面的key方法自动失效,这个键生成器是:
* org.springframework.cache.interceptor.KeyGenerator,函数式接口
* 只有一个generate方法,我们通过自定义的逻辑来实现自定义的key生成策略
*/
String keyGenerator() default "";
/**
* 设置自定义的CacheManager(缓存管理器),自动生成一个CacheResolver(缓存解析器)
* 与cacheResolver()方法设置互斥
*/
String cacheManager() default "";
/** 设置一个自定义的缓存解析器 */
String cacheResolver() default "";
/**
* 设置执行缓存的条件,如果条件不满足,方法返回的结果就不会被缓存,默认无条件全部缓存
* 使用SpEL(Spring Expression Language)来定义条件
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String condition() default "";
/**
* 用于禁用缓存功能,如果设置的条件满足,就不缓存结果
* 与上面condition()方法不同之处在于,该方法执行在当前方法调用结束,结果出来之后,
* 因此它除了可以使用上面condition所能使用的SpEl表达式外
* 还可以使用#result来获取方法的执行结果,根据结果的不同来决定是否缓存
*/
String unless() default "";
}
示列:
@Service
@Log4j2
public class AnimalService {
@Autowired
private AnimalRepository animalRepository;
@CachePut(value = "animalById", key = "#animal.id")
public ResponseEntity<Animal> updateAnimal(final Animal animal){
Wrapper<Animal> animalWrapper = new UpdateWrapper<>();
((UpdateWrapper<Animal>) animalWrapper).eq("id",animal.getId());
animalRepository.update(animal, animalWrapper);
return ResponseEntity.ok(this.getAnimalById(animal.getId()));
}
}
这里指定更新缓存,value是缓存名称,更新上文@Cacheable示列方法 getAnimalById查询操作的同一缓存,而且key设置为id也与上面的key设置对应
@CacheEvict
删除缓存
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
/**指定缓存名称,与cacheNames()方法效果一样*/
@AliasFor("cacheNames")
String[] value() default {};
/** 指定缓存名称,与vale()方法效果一样*/
@AliasFor("value")
String[] cacheNames() default {};
/**
* 使用SpEL(Spring Expression Language)表达式手动指定缓存键的组合方式,默认情况使用所有的参数来组合成键,除非自定义了keyGenerator
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String key() default "";
/**
* 自定义键生成器,定义了该方法后,上面的key方法自动失效,这个键生成器是:
* org.springframework.cache.interceptor.KeyGenerator,函数式接口
* 只有一个generate方法,我们通过自定义的逻辑来实现自定义的key生成策略
*/
String keyGenerator() default "";
/**
* 设置自定义的CacheManager(缓存管理器),自动生成一个CacheResolver(缓存解析器)
* 与cacheResolver()方法设置互斥
*/
String cacheManager() default "";
/** 设置一个自定义的缓存解析器 */
String cacheResolver() default "";
/**
* 设置执行缓存的条件,如果条件不满足,方法返回的结果就不会被缓存,默认无条件全部缓存
* 使用SpEL(Spring Expression Language)来定义条件
* SpEl表达式可以根据上下文环境来获取到指定的数据
* #root.method:用于获取当前方法的Method实例
* #root.target:用于获取当前方法的target实例
* #root.caches:用于获取当前方法关联的缓存
* #root.methodName:用于获取当前方法的名称
* #root.targetClass:用于获取目标类类型
* #root.args[1]:获取当前方法的第二个参数,等同于:#p1和#a1和#argumentName
*/
String condition() default "";
/**指定当前缓存名称下的所有缓存是否全部删除,默认为false*/
boolean allEntries() default false;
/** 指定删除缓存的操作是否在方法调用之前完成,默认是false,表示先调用方法,再执行缓存删除*/
boolean beforeInvocation() default false;
}
示列:
@Service
@Log4j2
public class AnimalService {
@Autowired
private AnimalRepository animalRepository;
//...
@CacheEvict(value = "animalById", key = "#id")
public ResponseEntity<Integer> deleteAnimalById(final int id){
return ResponseEntity.ok(animalRepository.deleteById(id));
}
//...
}
@Caching
多个缓存操作的组合,包括针对不同缓存名称的相同操作等
源码:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
/**指定多个缓存设置操作*/
Cacheable[] cacheable() default {};
/**指定多个缓存更新操作*/
CachePut[] put() default {};
/**指定多个缓存失效操作*/
CacheEvict[] evict() default {};
}
示列:
@Service
@Log4j2
public class AnimalService {
@Autowired
private AnimalRepository animalRepository;
//...
@Caching(
evict = {
@CacheEvict(value = "animalById", key = "#id"),
@CacheEvict(value = "animals", allEntries = true, beforeInvocation = true)
}
)
public ResponseEntity<Integer> deleteAnimalById(final int id){
return ResponseEntity.ok(animalRepository.deleteById(id));
}
@Cacheable("animals")
public ResponseEntity<Page<Animal>> getAnimalPage(final Animal animal, final int pageId, final int pageSize){
Page<Animal> page = new Page<>();
page.setCurrent(pageId);
page.setSize(pageSize);
return ResponseEntity.ok((Page<Animal>) animalRepository.selectPage(page,packWrapper(animal, WrapperType.QUERY)));
}
//...
}
@CacheConfig
标注于类上,进行一些公共的缓存相关配置
源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
/**设置统一的缓存名,适用于整个类中的方法全部是针对同一缓存名操作的情况*/
String[] cacheNames() default {};
/**设置统一个键生成器,免去了每个缓存设置中单独设置*/
String keyGenerator() default "";
/**设置统一个自定义缓存管理器*/
String cacheManager() default "";
/**设置统一个自定义缓存解析器*/
String cacheResolver() default "";
}