本文是缓存系列第三篇,前两篇别离先容了 Guava 和 JetCache。
前两篇我们讲了 Guava 和 JetCache,它们都是缓存的详细实现,本日给各人阐明一下 Spring 框架自己对这些缓存详细实现的支持和融合。利用 Spring Cache 将大大的淘汰我们的Spring项目中缓存利用的巨大度,提高代码可读性。本文将从以下几个方面来认识Spring Cache框架。
配景
SpringCache 发生的配景其实与Spring发生的配景有点雷同。由于 Java EE 系统框架臃肿、低效,代码可观性低,工具建设和依赖干系巨大, Spring 框架出来了,今朝根基上所有的Java靠山项目都离不开 Spring 或 SpringBoot (对 Spring 的进一步简化)。此刻项目面对高并发的问题越来越多,种种缓存的应用也增多,那么在通用的 Spring 框架上,就需要有一种越发便捷简朴的方法,来完成缓存的支持,就这样 SpringCache就呈现了。
不外首先我们需要大白的一点是,昆山软件开发,SpringCache 并非某一种 Cache 实现的技能,SpringCache 是一种缓存实现的通用技能,基于 Spring 提供的 Cache 框架,让开拓者更容易将本身的缓存实现高效便捷的嵌入到本身的项目中。虽然,昆山软件开发,SpringCache 也提供了自己的简朴实现 NoOpCacheManager、ConcurrentMapCacheManager 等。通过 SpringCache,可以快速嵌入本身的Cache实现。
用法
源码已分享至Github:https://github.com/zhuzhenke/common-caches
留意点:
@Bean
@Qualifier("concurrentMapCacheManager")
@Primary
ConcurrentMapCacheManager concurrentMapCacheManager() {
return new ConcurrentMapCacheManager();
}
这里利用了 @Primary 和 @Qualifier 注解,@Qualifier 注解是给这个 Bean 加一个名字,用于同一个接口 Bean 的多个实现时,指定当前 Bean 的名字,也就意味着 CacheManager 可以设置多个,而且在差异的方刑场景下利用。@Primary 注解是当接口 Bean 有多个时,优先注入当前 Bean 。
此刻拿 CategoryService 实现来阐明。
public class CategoryService {
@Caching(evict = {@CacheEvict(value = CategoryCacheConstants.CATEGORY_DOMAIN,
key = "#category.getCategoryCacheKey()",
beforeInvocation = true)})
public int add(Category category) {
System.out.println("模仿举办数据库交互操纵......");
System.out.println("Cache became invalid,value:" + CategoryCacheConstants.CATEGORY_DOMAIN
+ ",key:" + category.getCategoryCacheKey());
return 1;
}
@Caching(evict = {@CacheEvict(value = CategoryCacheConstants.CATEGORY_DOMAIN,
key = "#category.getCategoryCacheKey()",
beforeInvocation = true)})
public int delete(Category category) {
System.out.println("模仿举办数据库交互操纵......");
System.out.println("Cache became invalid,value:" + CategoryCacheConstants.CATEGORY_DOMAIN
+ ",key:" + category.getCategoryCacheKey());
return 0;
}
@Caching(evict = {@CacheEvict(value = CategoryCacheConstants.CATEGORY_DOMAIN,
key = "#category.getCategoryCacheKey()")})
public int update(Category category) {
System.out.println("模仿举办数据库交互操纵......");
System.out.println("Cache updated,value:" + CategoryCacheConstants.CATEGORY_DOMAIN
+ ",key:" + category.getCategoryCacheKey()
+ ",category:" + category);
return 1;
}
@Cacheable(value = CategoryCacheConstants.CATEGORY_DOMAIN,
key = "#category.getCategoryCacheKey()")
public Category get(Category category) {
System.out.println("模仿举办数据库交互操纵......");
Category result = new Category();
result.setCateId(category.getCateId());
result.setCateName(category.getCateId() + "CateName");
result.setParentId(category.getCateId() - 10);
return result;
}
}
CategoryService 通过对 category 工具的数据库增删改查,模仿缓存失效缓和存增加的功效。利用很是轻便,把注解加在要领上,则可以到达缓存的生效和失效方案。
深入源码
源码阐明我们分为几个方面一步一步表明个中的实现道理和实现细节。源码基于 Spring 4.3.7.RELEASE 阐明。
SpringCache 在要领上利用注解发挥缓存的浸染,缓存的发明是基于 AOP 的 PointCut 和 MethodMatcher 通过在注入的 class 中找到每个要领上的注解,并理会出来。
首先看到 org.springframework.cache.annotation.SpringCacheAnnotationParser 类:
protected Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> ops = null;
Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
if (!cacheables.isEmpty()) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
}
Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
if (!evicts.isEmpty()) {
ops = lazyInit(ops);
for (CacheEvict evict : evicts) {
ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
}
}
Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
if (!puts.isEmpty()) {
ops = lazyInit(ops);
for (CachePut put : puts) {
ops.add(parsePutAnnotation(ae, cachingConfig, put));
}
}
Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
if (!cachings.isEmpty()) {
ops = lazyInit(ops);
for (Caching caching : cachings) {
Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
if (cachingOps != null) {
ops.addAll(cachingOps);
}
}
}
return ops;
}