前言
本文算是了解缓存注解原理的先行文章,因为它抽象出来的模块类比较多,所以做这篇文章进行关键类的打点。
若我们需要扩展缓存注解的能力,对这些抽象是非常有必要深入了解的~
Spring内置的三大注解缓存是:
Cacheable
:缓存CacheEvict
:删除缓存CachePut
:更新缓存
CacheOperation:缓存操作
它是缓存操作的基类。我们知道不同的缓存注解,都有不同的缓存操作并且注解内的属性也挺多,此类就是对缓存操作的抽象
代码语言:javascript复制// @since 3.1 三大缓存注解属性的基类~~~
public abstract class CacheOperation implements BasicOperation {
private final String name;
private final Set<String> cacheNames;
private final String key;
private final String keyGenerator;
private final String cacheManager;
private final String cacheResolver;
private final String condition;
// 把toString()作为一个成员变量记着了 因为调用的次数太多
private final String toString;
// 构造方法是protected 的~~~ 入参一个Builder来对各个属性赋值
// builder方式是@since 4.3提供的,显然Spring4.3对这部分进行了改造~
protected CacheOperation(Builder b) {
this.name = b.name;
this.cacheNames = b.cacheNames;
this.key = b.key;
this.keyGenerator = b.keyGenerator;
this.cacheManager = b.cacheManager;
this.cacheResolver = b.cacheResolver;
this.condition = b.condition;
this.toString = b.getOperationDescription().toString();
}
... // 省略所有的get/set(其实builder里都只有set方法~~~)
// 它的public静态抽象内部类 Builder 简单的说,它是构建一个CacheOperation的构建器
public abstract static class Builder {
private String name = "";
private Set<String> cacheNames = Collections.emptySet();
private String key = "";
private String keyGenerator = "";
private String cacheManager = "";
private String cacheResolver = "";
private String condition = "";
// 省略所有的get/set
// 抽象方法 自行实现~
public abstract CacheOperation build();
}
}
// @since 4.1
public interface BasicOperation {
Set<String> getCacheNames();
}
它的继承图谱如下:
对应着三个注解,Spring提供了三种不同的操作实现。基类CacheOperation
里封装的是三哥们都共有的属性,所以实现类里处理各自的个性化属性
// @since 3.1
public class CacheableOperation extends CacheOperation {
@Nullable
private final String unless;
private final boolean sync;
public CacheableOperation(CacheableOperation.Builder b) {
super(b);
this.unless = b.unless;
this.sync = b.sync;
}
... // 省略get方法(无set方法哦)
// @since 4.3
public static class Builder extends CacheOperation.Builder {
@Nullable
private String unless;
private boolean sync;
... // 生路set方法(没有get方法哦~)
// Spring4.3抽象的这个技巧还是不错的,此处传this进去即可
@Override
public CacheableOperation build() {
return new CacheableOperation(this);
}
@Override
protected StringBuilder getOperationDescription() {
StringBuilder sb = super.getOperationDescription();
sb.append(" | unless='");
sb.append(this.unless);
sb.append("'");
sb.append(" | sync='");
sb.append(this.sync);
sb.append("'");
return sb;
}
}
}
因为三哥们的实现完全一样,所以此处只需要举
CacheableOperation
这一个例子即可
CacheOperationSource:缓存属性源
缓存属性源
。该接口被CacheInterceptor
它使用。它能够获取到Method
上所有的缓存操作集合:
// @since 3.1
public interface CacheOperationSource {
// 返回此Method方法上面所有的缓存操作~~~~CacheOperation 集合
// 显然一个Method上可以对应有多个缓存操作~~~~
@Nullable
Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);
}
它的继承树如下:
这里面有最为重要的AnnotationCacheOperationSource
,以及还有NameMatchCacheOperationSource
根据名称匹配的实现。
NameMatchCacheOperationSource
根据方法名称来匹配看看作用在此方法上的缓存操作有哪些~(不需要注解了)
代码语言:javascript复制// @since 3.1
public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable {
/** Keys are method names; values are TransactionAttributes. */
private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
// 你配置的时候,可以调用此方法。这里使用的也是add方法
// 先拦截这个方法,然后看看这个方法有木有匹配的缓存操作,有点想AOP的配置
public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) {
nameMap.forEach(this::addCacheMethod);
}
public void addCacheMethod(String methodName, Collection<CacheOperation> ops) {
// 输出debug日志
if (logger.isDebugEnabled()) {
logger.debug("Adding method [" methodName "] with cache operations [" ops "]");
}
this.nameMap.put(methodName, ops);
}
// 这部分逻辑挺简单,就是根据方法去Map类里匹配到一个最为合适的Collection<CacheOperation>
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
// look for direct name match
String methodName = method.getName();
Collection<CacheOperation> ops = this.nameMap.get(methodName);
// 若不是null就直接return了~ 首次进来,都会进入到这个逻辑~~~~
if (ops == null) {
// Look for most specific name match. // 找打一个最适合的 最匹配的
String bestNameMatch = null;
// 遍历所有外部已经制定进来了的方法名们~~~~
for (String mappedName : this.nameMap.keySet()) {
// isMatch就是Ant风格匹配~~(第一步) 光Ant匹配上了还不算
// 第二步:bestNameMatch=null或者
if (isMatch(methodName, mappedName)
&& (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
// mappedName为匹配上了的名称~~~~ 给bestNameMatch 赋值
// 继续循环,最终找到一个最佳的匹配~~~~
ops = this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}
return ops;
}
protected boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
...
}
这个Collection<CacheOperation>
的匹配规则很简单,就是使用methodName进行匹配的,支持Ant风格匹配模式。
AbstractFallbackCacheOperationSource
这个抽象方法主要目的是:让缓存注解(当然此抽象类并不要求一定是注解,别的方式也成)
既能使用在类上,也能使用在方法上。方法上没找到,就Fallback
到类上去找。
代码语言:javascript复制并且它还支持把注解写在接口上,哪怕你只是一个JDK动态代理的实现而已。比如我们的MyBatis Mapper接口上也是可以直接使用缓存注解的~
// @since 3.1
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
// 这个Map初始值可不小,因为它要缓存所有的Method
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
// 如果这个方法是Object的方法,那就不考虑缓存操作~~~
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 以method和Class作为上面缓存Map的key
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
}
// 核心处理逻辑:包括AnnotationCacheOperationSource的主要逻辑也是沿用的这个模版
else {
// computeCacheOperations计算缓存属性,这个方法是本类的灵魂,见下面
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" method.getName() "' with attribute: " cacheOps);
}
this.attributeCache.put(cacheKey, cacheOps);
} else { // 若没有标注属性的方法,用NULL_CACHING_ATTRIBUTE占位~ 不用null值哦~~~~ Spring内部大都不直接使用null
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
}
@Nullable
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
// allowPublicMethodsOnly()默认是false(子类复写后的默认值已经写为true了)
// 也就是说:缓存注解只能标注在public方法上~~~~不接收别非public方法~~~
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
// 第一步:先去该方法上找,看看有木有啥缓存属性 有就返回
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
}
// Second try is the caching operation on the target class.
// 第二步:方法上没有,就再去方法所在的类上去找。
// isUserLevelMethod:我们自己书写的方法(非自动生成的) 才直接return,否则继续处理
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
// 他俩不相等,说明method这个方法它是标注在接口上的,这里也给与了支持
// 此处透露的性息:我们的缓存注解也可以标注在接口方法上,比如MyBatis的接口上都是ok的~~~~
if (specificMethod != method) {
// Fallback is to look at the original method.
opDef = findCacheOperations(method);
if (opDef != null) {
return opDef;
}
// Last fallback is the class of the original method.
opDef = findCacheOperations(method.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
return null;
}
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
protected boolean allowPublicMethodsOnly() {
return false;
}
}
该抽象类提供的能力是:你的缓存属性可以放在方法上,方法上没有的话会去类上找,它有大名鼎鼎的实现类:AnnotationCacheOperationSource
AnnotationCacheOperationSource
从名字就可以看出,它是和缓存注解有关的缓存属性源。它能够处理上述的三大缓存注解。
代码语言:javascript复制// @since 3.1
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
// 是否只允许此注解标注在public方法上???下面有设置值为true
// 该属性只能通过构造函数赋值
private final boolean publicMethodsOnly;
private final Set<CacheAnnotationParser> annotationParsers;
// 默认就设置了publicMethodsOnly=true
public AnnotationCacheOperationSource() {
this(true);
}
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
}
...
// 也可以自己来实现注解的解析器。(比如我们要做自定义注解的话,可以这么搞~~~)
public AnnotationCacheOperationSource(CacheAnnotationParser... annotationParsers) {
this.publicMethodsOnly = true;
this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
}
// 这两个方法:核心事件都交给了CacheAnnotationParser.parseCacheAnnotations方法
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
}
@Override
@Nullable
protected Collection<CacheOperation> findCacheOperations(Method method) {
return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
}
// CacheOperationProvider就是一个函数式接口而已~~~类似Function~
// 这块determineCacheOperations() CacheOperationProvider接口的设计还是很巧妙的 可以学习一下
@Nullable
protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
Collection<CacheOperation> ops = null;
// 调用我们设置进来的所有的CacheAnnotationParser一个一个的处理~~~
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
// 理解这一块:说明我们方法上、类上是可以标注N个注解的,都会同时生效~~~最后都会combined
if (annOps != null) {
if (ops == null) {
ops = annOps;
} else {
Collection<CacheOperation> combined = new ArrayList<>(ops.size() annOps.size());
combined.addAll(ops);
combined.addAll(annOps);
ops = combined;
}
}
}
return ops;
}
}
它专用于处理三大缓存注解,就是获取标注在方法上的缓存注解们。
另外需要说明的是:若你想扩展你自己的缓存注解(比如加上超时时间TTL),你的处理器可以可以继承自AnnotationCacheOperationSource
,然后进行自己的扩展吧~
可以看到解析缓存注解这块,依托的一个处理器是:CacheAnnotationParser
,下面继续介绍。
CompositeCacheOperationSource
又是Composite
组合模式,此设计模式在Spring中大量存在。
public class CompositeCacheOperationSource implements CacheOperationSource, Serializable {
// 这里用的数组,表面只能赋值一次 并且只能通过构造函数赋值
private final CacheOperationSource[] cacheOperationSources;
public CompositeCacheOperationSource(CacheOperationSource... cacheOperationSources) {
this.cacheOperationSources = cacheOperationSources;
}
public final CacheOperationSource[] getCacheOperationSources() {
return this.cacheOperationSources;
}
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
Collection<CacheOperation> ops = null;
for (CacheOperationSource source : this.cacheOperationSources) {
Collection<CacheOperation> cacheOperations = source.getCacheOperations(method, targetClass);
if (cacheOperations != null) {
if (ops == null) {
ops = new ArrayList<>();
}
ops.addAll(cacheOperations);
}
}
return ops;
}
}
对它的解释就是,所以组合进取的属性源,最终都会叠加生效。
注意:它还是个抽象类哦~~~它的唯一实现如下(匿名内部类):
代码语言:javascript复制public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private CacheOperationSource cacheOperationSource;
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Override
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
...
}
CacheAnnotationParser
:缓存注解解析器
解析Spring三大注解缓存的策略接口~~~
代码语言:javascript复制// @since 3.1
public interface CacheAnnotationParser {
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Class<?> type);
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Method method);
}
Spring内建了一个唯一实现类:SpringCacheAnnotationParser
。它对把注解解析为缓存属性非常的重要。
// @since 3.1
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
private static final Set<Class<? extends Annotation>> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8);
// 它能处理的注解类型,一目了然
static {
CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class);
CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class);
CACHE_OPERATION_ANNOTATIONS.add(CachePut.class);
CACHE_OPERATION_ANNOTATIONS.add(Caching.class);
}
// 处理class和Method
// 使用DefaultCacheConfig,把它传给parseCacheAnnotations() 来给注解属性搞定默认值
// 至于为何自己要新new一个呢???其实是因为@CacheConfig它只能放在类上呀~~~(传Method也能获取到类)
// 返回值都可以为null(没有标注此注解方法、类 那肯定返回null啊)
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
// 找到方法/类上的注解们~~~
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
// 第三个参数传的false:表示接口的注解它也会看一下~~~
Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
// 若出现接口方法里也标了,实例方法里也标了,那就继续处理。让以实例方法里标注的为准~~~
if (ops != null && ops.size() > 1) {
// More than one operation found -> local declarations override interface-declared ones...
Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
if (localOps != null) {
return localOps;
}
}
return ops;
}
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
// localOnly=true,只看自己的不看接口的。false表示接口的也得看~
Collection<? extends Annotation> anns = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) : AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
// 这里为和写个1???因为绝大多数情况下,我们都只会标注一个注解~~
final Collection<CacheOperation> ops = new ArrayList<>(1);
// 这里的方法parseCacheableAnnotation/parsePutAnnotation等 说白了 就是把注解属性,转换封装成为`CacheOperation`对象~~
// 注意parseCachingAnnotation方法,相当于~把注解属性转换成了CacheOperation对象 下面以它为例介绍
anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
anns.stream().filter(ann -> ann instanceof CachePut).forEach(
ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
// CacheableOperation是抽象类CacheOperation的子类~
private CacheableOperation parseCacheableAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
// 这个builder是CacheOperation.Builder的子类,父类规定了所有注解通用的一些属性~~~
CacheableOperation.Builder builder = new CacheableOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheable.cacheNames());
builder.setCondition(cacheable.condition());
builder.setUnless(cacheable.unless());
builder.setKey(cacheable.key());
builder.setKeyGenerator(cacheable.keyGenerator());
builder.setCacheManager(cacheable.cacheManager());
builder.setCacheResolver(cacheable.cacheResolver());
builder.setSync(cacheable.sync());
// DefaultCacheConfig是本类的一个内部类,来处理buider,给他赋值默认值~~~ 比如默认的keyGenerator等等
defaultConfig.applyDefault(builder);
CacheableOperation op = builder.build();
validateCacheOperation(ae, op); // 校验。key和KeyGenerator至少得有一个 CacheManager和CacheResolver至少得配置一个
return op;
}
... // 解析其余注解略,一样的。
// 它其实就是把三三者聚合了,一个一个的遍历。所以它最后一个参数传的ops,在内部进行add
private void parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
Cacheable[] cacheables = caching.cacheable();
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
}
CacheEvict[] cacheEvicts = caching.evict();
for (CacheEvict cacheEvict : cacheEvicts) {
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
}
CachePut[] cachePuts = caching.put();
for (CachePut cachePut : cachePuts) {
ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
}
}
// 设置默认值的私有静态内部类
private static class DefaultCacheConfig {
private final Class<?> target;
@Nullable
private String[] cacheNames;
@Nullable
private String keyGenerator;
@Nullable
private String cacheManager;
@Nullable
private String cacheResolver;
private boolean initialized = false;
// 唯一构造函数~
public DefaultCacheConfig(Class<?> target) {
this.target = target;
}
public void applyDefault(CacheOperation.Builder builder) {
// 如果还没初始化过,就进行初始化(毕竟一个类上有多个方法,这种默认通用设置只需要来一次即可)
if (!this.initialized) {
// 找到类上、接口上的@CacheConfig注解 它可以指定本类使用的cacheNames和keyGenerator和cacheManager和cacheResolver
CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, CacheConfig.class);
if (annotation != null) {
this.cacheNames = annotation.cacheNames();
this.keyGenerator = annotation.keyGenerator();
this.cacheManager = annotation.cacheManager();
this.cacheResolver = annotation.cacheResolver();
}
this.initialized = true;
}
// 下面方法一句话总结:方法上没有指定的话,就用类上面的CacheConfig配置
if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
builder.setCacheNames(this.cacheNames);
}
if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) && StringUtils.hasText(this.keyGenerator)) {
builder.setKeyGenerator(this.keyGenerator);
}
if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
// One of these is set so we should not inherit anything
} else if (StringUtils.hasText(this.cacheResolver)) {
builder.setCacheResolver(this.cacheResolver);
} else if (StringUtils.hasText(this.cacheManager)) {
builder.setCacheManager(this.cacheManager);
}
}
}
}
经过这一番解析后,三大缓存注解,最终都被收集到CacheOperation
里来了,这也就和CacheOperationSource
缓存属性源接口的功能照应了起来。
CacheOperationInvocationContext:执行上下文
它代表缓存执行时的上下文。
代码语言:javascript复制//@since 4.1 注意泛型O extends BasicOperation
public interface CacheOperationInvocationContext<O extends BasicOperation> {
// 缓存操作属性CacheOperation
O getOperation();
// 目标类、目标方法
Object getTarget();
Method getMethod();
// 方法入参们
Object[] getArgs();
}
它只有一个CacheAspectSupport
内部类实现CacheOperationContext
,此处也搬上来吧:
protected class CacheOperationContext implements CacheOperationInvocationContext<CacheOperation> {
// 它里面包含了CacheOperation、Method、Class、Method targetMethod;(注意有两个Method)、AnnotatedElementKey、KeyGenerator、CacheResolver等属性
// this.method = BridgeMethodResolver.findBridgedMethod(method);
// this.targetMethod = (!Proxy.isProxyClass(targetClass) ? AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
// this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
private final CacheOperationMetadata metadata;
private final Object[] args;
private final Object target;
private final Collection<? extends Cache> caches;
private final Collection<String> cacheNames;
@Nullable
private Boolean conditionPassing; // 标志CacheOperation.conditon是否是true:表示通过 false表示未通过
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
this.metadata = metadata;
this.args = extractArgs(metadata.method, args);
this.target = target;
// 这里方法里调用了cacheResolver.resolveCaches(context)方法来得到缓存们~~~~ CacheResolver
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
// 从caches拿到他们的names们
this.cacheNames = createCacheNames(this.caches);
}
... // 省略get/set
protected boolean isConditionPassing(@Nullable Object result) {
if (this.conditionPassing == null) {
// 如果配置了并且还没被解析过,此处就解析condition条件~~~
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
// 执行上下文:此处就不得不提一个非常重要的它了:CacheOperationExpressionEvaluator
// 它代表着缓存操作中SpEL的执行上下文~~~ 具体可以先参与下面的对它的介绍
// 解析conditon~
EvaluationContext evaluationContext = createEvaluationContext(result);
this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
this.metadata.methodKey, evaluationContext);
} else {
this.conditionPassing = true;
}
}
return this.conditionPassing;
}
// 解析CacheableOperation和CachePutOperation好的unless
protected boolean canPutToCache(@Nullable Object value) {
String unless = "";
if (this.metadata.operation instanceof CacheableOperation) {
unless = ((CacheableOperation) this.metadata.operation).getUnless();
} else if (this.metadata.operation instanceof CachePutOperation) {
unless = ((CachePutOperation) this.metadata.operation).getUnless();
}
if (StringUtils.hasText(unless)) {
EvaluationContext evaluationContext = createEvaluationContext(value);
return !evaluator.unless(unless, this.metadata.methodKey, evaluationContext);
}
return true;
}
// 这里注意:生成key 需要注意步骤。
// 若配置了key(非空串):那就作为SpEL解析出来
// 否则走keyGenerator去生成~~~(所以你会发现,即使咱们没有配置key和keyGenerator,程序依旧能正常work,只是生成的key很长而已~~~)
// (keyGenerator你可以能没配置????)
// 若你自己没有手动指定过KeyGenerator,那会使用默认的SimpleKeyGenerator 它的实现比较简单
// 其实若我们自定义KeyGenerator,我觉得可以继承自`SimpleKeyGenerator `,而不是直接实现接口~~~
@Nullable
protected Object generateKey(@Nullable Object result) {
if (StringUtils.hasText(this.metadata.operation.getKey())) {
EvaluationContext evaluationContext = createEvaluationContext(result);
return evaluator.key(this.metadata.operation.getKey(), this.metadata.methodKey, evaluationContext);
}
// key的优先级第一位,没有指定采用生成器去生成~
return this.metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
}
private EvaluationContext createEvaluationContext(@Nullable Object result) {
return evaluator.createEvaluationContext(this.caches, this.metadata.method, this.args,
this.target, this.metadata.targetClass, this.metadata.targetMethod, result, beanFactory);
}
...
}
CacheOperationExpressionEvaluator:缓存操作执行器
在这之前,在讲解发布订阅、事件机制的文章中:
【小家Spring】从Spring中的(ApplicationEvent)事件驱动机制出发,聊聊【观察者模式】【监听者模式】【发布订阅模式】【消息队列MQ】【EventSourcing】…
讲到过EventExpressionEvaluator
,它在解析@EventListener
注解的condition
属性的时候被使用到,它也继承自抽象父类CachedExpressionEvaluator
:
可见本类也是抽象父类的一个实现,是不是顿时有种熟悉感了?
代码语言:javascript复制// @since 3.1 注意抽象父类CachedExpressionEvaluator在Spring4.2才有
// CachedExpressionEvaluator里默认使用的解析器是:SpelExpressionParser 以及
// 还准备了一个ParameterNameDiscoverer 可以交给执行上文CacheEvaluationContext使用
class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
// 注意这两个属性是public的 在CacheAspectSupport里都有使用~~~
public static final Object NO_RESULT = new Object();
public static final Object RESULT_UNAVAILABLE = new Object();
public static final String RESULT_VARIABLE = "result";
private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<>(64);
private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<>(64);
// 这个方法是创建执行上下文。能给解释:为何可以使用#result这个key的原因
// 此方法的入参确实不少:8个
public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
Method method, Object[] args, Object target, Class<?> targetClass, Method targetMethod,
@Nullable Object result, @Nullable BeanFactory beanFactory) {
// root对象,此对象的属性决定了你的#root能够取值的范围,具体下面有贴出此类~
CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches, method, args, target, targetClass);
// 它继承自MethodBasedEvaluationContext,已经讲解过了,本文就不继续深究了~
CacheEvaluationContext evaluationContext = new CacheEvaluationContext(rootObject, targetMethod, args, getParameterNameDiscoverer());
if (result == RESULT_UNAVAILABLE) {
evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
} else if (result != NO_RESULT) {
// 这一句话就是为何我们可以使用#result的核心原因~
evaluationContext.setVariable(RESULT_VARIABLE, result);
}
// 从这里可知,缓存注解里也是可以使用容器内的Bean的~
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
// 都有缓存效果哦,因为都继承自抽象父类CachedExpressionEvaluator嘛
// 抽象父类@since 4.2才出来,就是给解析过程提供了缓存的效果~~~~(注意此缓存非彼缓存)
// 解析key的SpEL表达式
@Nullable
public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return getExpression(this.keyCache, methodKey, keyExpression).getValue(evalContext);
}
// condition和unless的解析结果若不是bool类型,统一按照false处理~~~~
public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return (Boolean.TRUE.equals(getExpression(this.conditionCache, methodKey, conditionExpression).getValue(evalContext, Boolean.class)));
}
public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return (Boolean.TRUE.equals(getExpression(this.unlessCache, methodKey, unlessExpression).getValue(evalContext, Boolean.class)));
}
/**
* Clear all caches.
*/
void clear() {
this.keyCache.clear();
this.conditionCache.clear();
this.unlessCache.clear();
}
}
// #root可取的值,参考CacheExpressionRootObject这个对象的属性
// @since 3.1
class CacheExpressionRootObject {
// 可见#root.caches竟然都是阔仪的~~~
private final Collection<? extends Cache> caches;
private final Method method;
private final Object[] args;
private final Object target;
private final Class<?> targetClass;
// 省略所有的get/set(其实只有get方法)
}
缓存操作的执行器,能让你深刻的理解到#root
、#result
的使用,并且永远忘记不了了。
CacheResolver
其名字已经暗示了其是Cache
解析器,用于根据实际情况来动态解析使用哪个Cache
,它是Spring4.1
提供的新特性。
// @since 4.1
@FunctionalInterface
public interface CacheResolver {
// 根据执行上下文,拿到最终的缓存Cache集合
// CacheOperationInvocationContext:执行上下文
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
}
它有一些内置实现如下:
AbstractCacheResolver
具体实现根据调用上下文提供缓存名称集合。
代码语言:javascript复制// @since 4.1 实现了InitializingBean
public abstract class AbstractCacheResolver implements CacheResolver, InitializingBean {
// 课件它还是依赖于CacheManager的
@Nullable
private CacheManager cacheManager;
// 构造函数统一protected
protected AbstractCacheResolver() {
}
protected AbstractCacheResolver(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
// 设置,指定一个CacheManager
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public CacheManager getCacheManager() {
Assert.state(this.cacheManager != null, "No CacheManager set");
return this.cacheManager;
}
// 做了一步校验而已~~~CacheManager 必须存在
// 这是一个使用技巧哦 自己的在设计框架的框架的时候可以使用~
@Override
public void afterPropertiesSet() {
Assert.notNull(this.cacheManager, "CacheManager is required");
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
// getCacheNames抽象方法,子类去实现~~~~
Collection<String> cacheNames = getCacheNames(context);
if (cacheNames == null) {
return Collections.emptyList();
}
// 根据cacheNames 去CacheManager里面拿到Cache对象, 作为最终的返回
Collection<Cache> result = new ArrayList<>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache cache = getCacheManager().getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named '"
cacheName "' for " context.getOperation());
}
result.add(cache);
}
return result;
}
// 子类需要实现此抽象方法
@Nullable
protected abstract Collection<String> getCacheNames(CacheOperationInvocationContext<?> context);
}
此抽象类一样的,只是模版实现了resolveCaches()
方法。抽象方法的实现交给了实现类
SimpleCacheResolver
代码语言:javascript复制public class SimpleCacheResolver extends AbstractCacheResolver {
...
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
return context.getOperation().getCacheNames();
}
...
}
这个实现太简单了,没啥好说的。
NamedCacheResolver
代码语言:javascript复制public class NamedCacheResolver extends AbstractCacheResolver {
@Nullable
private Collection<String> cacheNames;
public NamedCacheResolver() {
}
public NamedCacheResolver(CacheManager cacheManager, String... cacheNames) {
super(cacheManager);
this.cacheNames = new ArrayList<>(Arrays.asList(cacheNames));
}
/**
* Set the cache name(s) that this resolver should use.
*/
public void setCacheNames(Collection<String> cacheNames) {
this.cacheNames = cacheNames;
}
@Override
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
return this.cacheNames;
}
}
此解析器的特点是,可以自己setCacheNames()
。
若内建的不符合条件,我们可以自己实现一个自己的**CacheResolver
**。比如实现和业务相关的缓存处理器(若Class==某Class,做些特殊的操作之类的)
需要注意的是:即使你配置使用的是
CacheResolver
,你也必须在配置里提供cacheNames至少一个的,因为毕竟是根据你配置的CacheNames去CacheManager里找(当然,若是你的自定义实现除外)
CacheOperationSourcePointcut
Pointcut
小伙伴应该不陌生,在AOP
章节重点又重点的描述过,我们知道Pointcut直接关乎到是否要生成代理对象,所以此类还是蛮重要的。
// @since 3.1 它是个StaticMethodMatcherPointcut 所以方法入参它不关心
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
// 如果你这个类就是一个CacheManager,不切入
@Override
public boolean matches(Method method, Class<?> targetClass) {
if (CacheManager.class.isAssignableFrom(targetClass)) {
return false;
}
// 获取到当前的缓存属性源~~~getCacheOperationSource()是个抽象方法
CacheOperationSource cas = getCacheOperationSource();
// 下面一句话解释为:如果方法/类上标注有缓存相关的注解,就切入进取~~
// 具体逻辑请参见方法:cas.getCacheOperations();
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CacheOperationSourcePointcut)) {
return false;
}
CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
}
@Override
public int hashCode() {
return CacheOperationSourcePointcut.class.hashCode();
}
@Override
public String toString() {
return getClass().getName() ": " getCacheOperationSource();
}
/**
* Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
* To be implemented by subclasses.
*/
@Nullable
protected abstract CacheOperationSource getCacheOperationSource();
}
CacheErrorHandler
处理缓存发生错误时的策略接口。在大多数情况下,提供者抛出的任何异常都应该简单地被抛出到客户机上,但是在某些情况下,基础结构可能需要以不同的方式处理缓存提供者异常。这个时候可以使用此接口来处理
接口内容十分之简单:
代码语言:javascript复制public interface CacheErrorHandler {
void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
void handleCachePutError(RuntimeException exception, Cache cache, Object key, @Nullable Object value);
void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
void handleCacheClearError(RuntimeException exception, Cache cache);
}
Spring对它唯一内建实现为SimpleCacheErrorHandler
,代码我不贴了,全是空实现
,所以它是一个Adapter
适配器形式的存在。
若你想自己提供CacheErrorHandler
,你可以继承自SimpleCacheErrorHandler
来弄~~~
AbstractCacheInvoker
它的作用是在进行缓存操作时发生异常时,调用组件CacheErrorHandler
来进行处理~
// @since 4.1
public abstract class AbstractCacheInvoker {
//protected属性~
protected SingletonSupplier<CacheErrorHandler> errorHandler;
protected AbstractCacheInvoker() {
this.errorHandler = SingletonSupplier.of(SimpleCacheErrorHandler::new);
}
protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
this.errorHandler = SingletonSupplier.of(errorHandler);
}
// 此处用的obtain方法 它能保证不为null
public CacheErrorHandler getErrorHandler() {
return this.errorHandler.obtain();
}
@Nullable
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
try {
return cache.get(key);
} catch (RuntimeException ex) {
getErrorHandler().handleCacheGetError(ex, cache, key);
// 只有它有返回值哦 返回null
return null; // If the exception is handled, return a cache miss
}
}
... // 省略doPut、doEvict、doClear 处理方式同上
}
可见这个类在Spring4.1提出,专门用于处理异常的,毕竟CacheErrorHandler
也是Spring4.1后才有。
总结
本篇文章为讲解缓存注解的深入原理分析进行铺垫,所以密切关注这篇文章:
【小家Spring】玩转Spring Cache — @Cacheable/@CachePut/@CacheEvict注解的使用以及原理深度剖析