玩转Spring Cache --- @Cacheable/@CachePut/@CacheEvict缓存注解相关基础类打点【享学Spring】

不念不忘少年蓝@ 2021-12-15 01:47 443阅读 0赞

每篇一句

写书和翻译最重要的是把事做好,而不是为了让书上有你的名字

前言

本文算是了解缓存注解原理的先行文章,因为它抽象出来的模块类比较多,所以做这篇文章进行关键类的打点。
若我们需要扩展缓存注解的能力,对这些抽象是非常有必要深入了解的~

Spring内置的三大注解缓存是:

  1. Cacheable:缓存
  2. CacheEvict:删除缓存
  3. CachePut:更新缓存

CacheOperation:缓存操作

它是缓存操作的基类。我们知道不同的缓存注解,都有不同的缓存操作并且注解内的属性也挺多,此类就是对缓存操作的抽象

  1. // @since 3.1 三大缓存注解属性的基类~~~
  2. public abstract class CacheOperation implements BasicOperation {
  3. private final String name;
  4. private final Set<String> cacheNames;
  5. private final String key;
  6. private final String keyGenerator;
  7. private final String cacheManager;
  8. private final String cacheResolver;
  9. private final String condition;
  10. // 把toString()作为一个成员变量记着了 因为调用的次数太多
  11. private final String toString;
  12. // 构造方法是protected 的~~~ 入参一个Builder来对各个属性赋值
  13. // builder方式是@since 4.3提供的,显然Spring4.3对这部分进行了改造~
  14. protected CacheOperation(Builder b) {
  15. this.name = b.name;
  16. this.cacheNames = b.cacheNames;
  17. this.key = b.key;
  18. this.keyGenerator = b.keyGenerator;
  19. this.cacheManager = b.cacheManager;
  20. this.cacheResolver = b.cacheResolver;
  21. this.condition = b.condition;
  22. this.toString = b.getOperationDescription().toString();
  23. }
  24. ... // 省略所有的get/set(其实builder里都只有set方法~~~)
  25. // 它的public静态抽象内部类 Builder 简单的说,它是构建一个CacheOperation的构建器
  26. public abstract static class Builder {
  27. private String name = "";
  28. private Set<String> cacheNames = Collections.emptySet();
  29. private String key = "";
  30. private String keyGenerator = "";
  31. private String cacheManager = "";
  32. private String cacheResolver = "";
  33. private String condition = "";
  34. // 省略所有的get/set
  35. // 抽象方法 自行实现~
  36. public abstract CacheOperation build();
  37. }
  38. }
  39. // @since 4.1
  40. public interface BasicOperation {
  41. Set<String> getCacheNames();
  42. }

它的继承图谱如下:
在这里插入图片描述
对应着三个注解,Spring提供了三种不同的操作实现。基类CacheOperation里封装的是三哥们都共有的属性,所以实现类里处理各自的个性化属性~~~~

  1. // @since 3.1
  2. public class CacheableOperation extends CacheOperation {
  3. @Nullable
  4. private final String unless;
  5. private final boolean sync;
  6. public CacheableOperation(CacheableOperation.Builder b) {
  7. super(b);
  8. this.unless = b.unless;
  9. this.sync = b.sync;
  10. }
  11. ... // 省略get方法(无set方法哦)
  12. // @since 4.3
  13. public static class Builder extends CacheOperation.Builder {
  14. @Nullable
  15. private String unless;
  16. private boolean sync;
  17. ... // 生路set方法(没有get方法哦~)
  18. // Spring4.3抽象的这个技巧还是不错的,此处传this进去即可
  19. @Override
  20. public CacheableOperation build() {
  21. return new CacheableOperation(this);
  22. }
  23. @Override
  24. protected StringBuilder getOperationDescription() {
  25. StringBuilder sb = super.getOperationDescription();
  26. sb.append(" | unless='");
  27. sb.append(this.unless);
  28. sb.append("'");
  29. sb.append(" | sync='");
  30. sb.append(this.sync);
  31. sb.append("'");
  32. return sb;
  33. }
  34. }
  35. }

因为三哥们的实现完全一样,所以此处只需要举CacheableOperation这一个例子即可

CacheOperationSource:缓存属性源

缓存属性源。该接口被CacheInterceptor它使用。它能够获取到Method上所有的缓存操作集合:

  1. // @since 3.1
  2. public interface CacheOperationSource {
  3. // 返回此Method方法上面所有的缓存操作~~~~CacheOperation 集合
  4. // 显然一个Method上可以对应有多个缓存操作~~~~
  5. @Nullable
  6. Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);
  7. }

它的继承树如下:
在这里插入图片描述
这里面有最为重要的AnnotationCacheOperationSource,以及还有NameMatchCacheOperationSource根据名称匹配的实现。

NameMatchCacheOperationSource

根据方法名称来匹配看看作用在此方法上的缓存操作有哪些~(不需要注解了)

  1. // @since 3.1
  2. public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable {
  3. /** Keys are method names; values are TransactionAttributes. */
  4. private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<>();
  5. // 你配置的时候,可以调用此方法。这里使用的也是add方法
  6. // 先拦截这个方法,然后看看这个方法有木有匹配的缓存操作,有点想AOP的配置
  7. public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) {
  8. nameMap.forEach(this::addCacheMethod);
  9. }
  10. public void addCacheMethod(String methodName, Collection<CacheOperation> ops) {
  11. // 输出debug日志
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("Adding method [" + methodName + "] with cache operations [" + ops + "]");
  14. }
  15. this.nameMap.put(methodName, ops);
  16. }
  17. // 这部分逻辑挺简单,就是根据方法去Map类里匹配到一个最为合适的Collection<CacheOperation>
  18. @Override
  19. @Nullable
  20. public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
  21. // look for direct name match
  22. String methodName = method.getName();
  23. Collection<CacheOperation> ops = this.nameMap.get(methodName);
  24. // 若不是null就直接return了~ 首次进来,都会进入到这个逻辑~~~~
  25. if (ops == null) {
  26. // Look for most specific name match. // 找打一个最适合的 最匹配的
  27. String bestNameMatch = null;
  28. // 遍历所有外部已经制定进来了的方法名们~~~~
  29. for (String mappedName : this.nameMap.keySet()) {
  30. // isMatch就是Ant风格匹配~~(第一步) 光Ant匹配上了还不算
  31. // 第二步:bestNameMatch=null或者
  32. if (isMatch(methodName, mappedName)
  33. && (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
  34. // mappedName为匹配上了的名称~~~~ 给bestNameMatch 赋值
  35. // 继续循环,最终找到一个最佳的匹配~~~~
  36. ops = this.nameMap.get(mappedName);
  37. bestNameMatch = mappedName;
  38. }
  39. }
  40. }
  41. return ops;
  42. }
  43. protected boolean isMatch(String methodName, String mappedName) {
  44. return PatternMatchUtils.simpleMatch(mappedName, methodName);
  45. }
  46. ...
  47. }

这个Collection<CacheOperation>的匹配规则很简单,就是使用methodName进行匹配的,支持Ant风格匹配模式。

AbstractFallbackCacheOperationSource

这个抽象方法主要目的是:让缓存注解(当然此抽象类并不要求一定是注解,别的方式也成)既能使用在类上,也能使用在方法上。方法上没找到,就Fallback到类上去找。

并且它还支持把注解写在接口上,哪怕你只是一个JDK动态代理的实现而已。比如我们的MyBatis Mapper接口上也是可以直接使用缓存注解的~

  1. // @since 3.1
  2. public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
  3. private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
  4. // 这个Map初始值可不小,因为它要缓存所有的Method
  5. private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
  6. @Override
  7. @Nullable
  8. public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
  9. // 如果这个方法是Object的方法,那就不考虑缓存操作~~~
  10. if (method.getDeclaringClass() == Object.class) {
  11. return null;
  12. }
  13. // 以method和Class作为上面缓存Map的key
  14. Object cacheKey = getCacheKey(method, targetClass);
  15. Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
  16. if (cached != null) {
  17. return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
  18. }
  19. // 核心处理逻辑:包括AnnotationCacheOperationSource的主要逻辑也是沿用的这个模版
  20. else {
  21. // computeCacheOperations计算缓存属性,这个方法是本类的灵魂,见下面
  22. Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
  23. if (cacheOps != null) {
  24. if (logger.isTraceEnabled()) {
  25. logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
  26. }
  27. this.attributeCache.put(cacheKey, cacheOps);
  28. } else { // 若没有标注属性的方法,用NULL_CACHING_ATTRIBUTE占位~ 不用null值哦~~~~ Spring内部大都不直接使用null
  29. this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
  30. }
  31. return cacheOps;
  32. }
  33. }
  34. @Nullable
  35. private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
  36. // Don't allow no-public methods as required.
  37. // allowPublicMethodsOnly()默认是false(子类复写后的默认值已经写为true了)
  38. // 也就是说:缓存注解只能标注在public方法上~~~~不接收别非public方法~~~
  39. if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
  40. return null;
  41. }
  42. // The method may be on an interface, but we need attributes from the target class.
  43. // If the target class is null, the method will be unchanged.
  44. Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
  45. // First try is the method in the target class.
  46. // 第一步:先去该方法上找,看看有木有啥缓存属性 有就返回
  47. Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
  48. if (opDef != null) {
  49. return opDef;
  50. }
  51. // Second try is the caching operation on the target class.
  52. // 第二步:方法上没有,就再去方法所在的类上去找。
  53. // isUserLevelMethod:我们自己书写的方法(非自动生成的) 才直接return,否则继续处理
  54. opDef = findCacheOperations(specificMethod.getDeclaringClass());
  55. if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
  56. return opDef;
  57. }
  58. // 他俩不相等,说明method这个方法它是标注在接口上的,这里也给与了支持
  59. // 此处透露的性息:我们的缓存注解也可以标注在接口方法上,比如MyBatis的接口上都是ok的~~~~
  60. if (specificMethod != method) {
  61. // Fallback is to look at the original method.
  62. opDef = findCacheOperations(method);
  63. if (opDef != null) {
  64. return opDef;
  65. }
  66. // Last fallback is the class of the original method.
  67. opDef = findCacheOperations(method.getDeclaringClass());
  68. if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
  69. return opDef;
  70. }
  71. }
  72. return null;
  73. }
  74. @Nullable
  75. protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
  76. @Nullable
  77. protected abstract Collection<CacheOperation> findCacheOperations(Method method);
  78. protected boolean allowPublicMethodsOnly() {
  79. return false;
  80. }
  81. }

该抽象类提供的能力是:你的缓存属性可以放在方法上,方法上没有的话会去类上找,它有大名鼎鼎的实现类:AnnotationCacheOperationSource

AnnotationCacheOperationSource

从名字就可以看出,它是和缓存注解有关的缓存属性源。它能够处理上述的三大缓存注解。

  1. // @since 3.1
  2. public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
  3. // 是否只允许此注解标注在public方法上???下面有设置值为true
  4. // 该属性只能通过构造函数赋值
  5. private final boolean publicMethodsOnly;
  6. private final Set<CacheAnnotationParser> annotationParsers;
  7. // 默认就设置了publicMethodsOnly=true
  8. public AnnotationCacheOperationSource() {
  9. this(true);
  10. }
  11. public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
  12. this.publicMethodsOnly = publicMethodsOnly;
  13. this.annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
  14. }
  15. ...
  16. // 也可以自己来实现注解的解析器。(比如我们要做自定义注解的话,可以这么搞~~~)
  17. public AnnotationCacheOperationSource(CacheAnnotationParser... annotationParsers) {
  18. this.publicMethodsOnly = true;
  19. this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
  20. }
  21. // 这两个方法:核心事件都交给了CacheAnnotationParser.parseCacheAnnotations方法
  22. @Override
  23. @Nullable
  24. protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
  25. return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
  26. }
  27. @Override
  28. @Nullable
  29. protected Collection<CacheOperation> findCacheOperations(Method method) {
  30. return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
  31. }
  32. // CacheOperationProvider就是一个函数式接口而已~~~类似Function~
  33. // 这块determineCacheOperations() + CacheOperationProvider接口的设计还是很巧妙的 可以学习一下
  34. @Nullable
  35. protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
  36. Collection<CacheOperation> ops = null;
  37. // 调用我们设置进来的所有的CacheAnnotationParser一个一个的处理~~~
  38. for (CacheAnnotationParser annotationParser : this.annotationParsers) {
  39. Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
  40. // 理解这一块:说明我们方法上、类上是可以标注N个注解的,都会同时生效~~~最后都会combined
  41. if (annOps != null) {
  42. if (ops == null) {
  43. ops = annOps;
  44. } else {
  45. Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
  46. combined.addAll(ops);
  47. combined.addAll(annOps);
  48. ops = combined;
  49. }
  50. }
  51. }
  52. return ops;
  53. }
  54. }

它专用于处理三大缓存注解,就是获取标注在方法上的缓存注解们。
另外需要说明的是:若你想扩展你自己的缓存注解(比如加上超时时间TTL),你的处理器可以可以继承自AnnotationCacheOperationSource,然后进行自己的扩展吧~

可以看到解析缓存注解这块,依托的一个处理器是:CacheAnnotationParser,下面继续介绍。

CompositeCacheOperationSource

又是Composite组合模式,此设计模式在Spring中大量存在。

  1. public class CompositeCacheOperationSource implements CacheOperationSource, Serializable {
  2. // 这里用的数组,表面只能赋值一次 并且只能通过构造函数赋值
  3. private final CacheOperationSource[] cacheOperationSources;
  4. public CompositeCacheOperationSource(CacheOperationSource... cacheOperationSources) {
  5. this.cacheOperationSources = cacheOperationSources;
  6. }
  7. public final CacheOperationSource[] getCacheOperationSources() {
  8. return this.cacheOperationSources;
  9. }
  10. @Override
  11. @Nullable
  12. public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
  13. Collection<CacheOperation> ops = null;
  14. for (CacheOperationSource source : this.cacheOperationSources) {
  15. Collection<CacheOperation> cacheOperations = source.getCacheOperations(method, targetClass);
  16. if (cacheOperations != null) {
  17. if (ops == null) {
  18. ops = new ArrayList<>();
  19. }
  20. ops.addAll(cacheOperations);
  21. }
  22. }
  23. return ops;
  24. }
  25. }

对它的解释就是,所以组合进取的属性源,最终都会叠加生效
注意:它还是个抽象类哦~~~它的唯一实现如下(匿名内部类):

  1. public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
  2. @Nullable
  3. private CacheOperationSource cacheOperationSource;
  4. private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
  5. @Override
  6. @Nullable
  7. protected CacheOperationSource getCacheOperationSource() {
  8. return cacheOperationSource;
  9. }
  10. };
  11. ...
  12. }

CacheAnnotationParser:缓存注解解析器

解析Spring三大注解缓存的策略接口~~~

  1. // @since 3.1
  2. public interface CacheAnnotationParser {
  3. @Nullable
  4. Collection<CacheOperation> parseCacheAnnotations(Class<?> type);
  5. @Nullable
  6. Collection<CacheOperation> parseCacheAnnotations(Method method);
  7. }

Spring内建了一个唯一实现类:SpringCacheAnnotationParser。它对把注解解析为缓存属性非常的重要。

  1. // @since 3.1
  2. public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
  3. private static final Set<Class<? extends Annotation>> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8);
  4. // 它能处理的注解类型,一目了然
  5. static {
  6. CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class);
  7. CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class);
  8. CACHE_OPERATION_ANNOTATIONS.add(CachePut.class);
  9. CACHE_OPERATION_ANNOTATIONS.add(Caching.class);
  10. }
  11. // 处理class和Method
  12. // 使用DefaultCacheConfig,把它传给parseCacheAnnotations() 来给注解属性搞定默认值
  13. // 至于为何自己要新new一个呢???其实是因为@CacheConfig它只能放在类上呀~~~(传Method也能获取到类)
  14. // 返回值都可以为null(没有标注此注解方法、类 那肯定返回null啊)
  15. @Override
  16. @Nullable
  17. public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
  18. DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
  19. return parseCacheAnnotations(defaultConfig, type);
  20. }
  21. @Override
  22. @Nullable
  23. public Collection<CacheOperation> parseCacheAnnotations(Method method) {
  24. DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
  25. return parseCacheAnnotations(defaultConfig, method);
  26. }
  27. // 找到方法/类上的注解们~~~
  28. private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
  29. // 第三个参数传的false:表示接口的注解它也会看一下~~~
  30. Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
  31. // 若出现接口方法里也标了,实例方法里也标了,那就继续处理。让以实例方法里标注的为准~~~
  32. if (ops != null && ops.size() > 1) {
  33. // More than one operation found -> local declarations override interface-declared ones...
  34. Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
  35. if (localOps != null) {
  36. return localOps;
  37. }
  38. }
  39. return ops;
  40. }
  41. private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
  42. // localOnly=true,只看自己的不看接口的。false表示接口的也得看~
  43. Collection<? extends Annotation> anns = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) : AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
  44. if (anns.isEmpty()) {
  45. return null;
  46. }
  47. // 这里为和写个1???因为绝大多数情况下,我们都只会标注一个注解~~
  48. final Collection<CacheOperation> ops = new ArrayList<>(1);
  49. // 这里的方法parseCacheableAnnotation/parsePutAnnotation等 说白了 就是把注解属性,转换封装成为`CacheOperation`对象~~
  50. // 注意parseCachingAnnotation方法,相当于~把注解属性转换成了CacheOperation对象 下面以它为例介绍
  51. anns.stream().filter(ann -> ann instanceof Cacheable).forEach(
  52. ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
  53. anns.stream().filter(ann -> ann instanceof CacheEvict).forEach(
  54. ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
  55. anns.stream().filter(ann -> ann instanceof CachePut).forEach(
  56. ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
  57. anns.stream().filter(ann -> ann instanceof Caching).forEach(
  58. ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
  59. return ops;
  60. }
  61. // CacheableOperation是抽象类CacheOperation的子类~
  62. private CacheableOperation parseCacheableAnnotation(
  63. AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
  64. // 这个builder是CacheOperation.Builder的子类,父类规定了所有注解通用的一些属性~~~
  65. CacheableOperation.Builder builder = new CacheableOperation.Builder();
  66. builder.setName(ae.toString());
  67. builder.setCacheNames(cacheable.cacheNames());
  68. builder.setCondition(cacheable.condition());
  69. builder.setUnless(cacheable.unless());
  70. builder.setKey(cacheable.key());
  71. builder.setKeyGenerator(cacheable.keyGenerator());
  72. builder.setCacheManager(cacheable.cacheManager());
  73. builder.setCacheResolver(cacheable.cacheResolver());
  74. builder.setSync(cacheable.sync());
  75. // DefaultCacheConfig是本类的一个内部类,来处理buider,给他赋值默认值~~~ 比如默认的keyGenerator等等
  76. defaultConfig.applyDefault(builder);
  77. CacheableOperation op = builder.build();
  78. validateCacheOperation(ae, op); // 校验。key和KeyGenerator至少得有一个 CacheManager和CacheResolver至少得配置一个
  79. return op;
  80. }
  81. ... // 解析其余注解略,一样的。
  82. // 它其实就是把三三者聚合了,一个一个的遍历。所以它最后一个参数传的ops,在内部进行add
  83. private void parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
  84. Cacheable[] cacheables = caching.cacheable();
  85. for (Cacheable cacheable : cacheables) {
  86. ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
  87. }
  88. CacheEvict[] cacheEvicts = caching.evict();
  89. for (CacheEvict cacheEvict : cacheEvicts) {
  90. ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
  91. }
  92. CachePut[] cachePuts = caching.put();
  93. for (CachePut cachePut : cachePuts) {
  94. ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
  95. }
  96. }
  97. // 设置默认值的私有静态内部类
  98. private static class DefaultCacheConfig {
  99. private final Class<?> target;
  100. @Nullable
  101. private String[] cacheNames;
  102. @Nullable
  103. private String keyGenerator;
  104. @Nullable
  105. private String cacheManager;
  106. @Nullable
  107. private String cacheResolver;
  108. private boolean initialized = false;
  109. // 唯一构造函数~
  110. public DefaultCacheConfig(Class<?> target) {
  111. this.target = target;
  112. }
  113. public void applyDefault(CacheOperation.Builder builder) {
  114. // 如果还没初始化过,就进行初始化(毕竟一个类上有多个方法,这种默认通用设置只需要来一次即可)
  115. if (!this.initialized) {
  116. // 找到类上、接口上的@CacheConfig注解 它可以指定本类使用的cacheNames和keyGenerator和cacheManager和cacheResolver
  117. CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, CacheConfig.class);
  118. if (annotation != null) {
  119. this.cacheNames = annotation.cacheNames();
  120. this.keyGenerator = annotation.keyGenerator();
  121. this.cacheManager = annotation.cacheManager();
  122. this.cacheResolver = annotation.cacheResolver();
  123. }
  124. this.initialized = true;
  125. }
  126. // 下面方法一句话总结:方法上没有指定的话,就用类上面的CacheConfig配置
  127. if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
  128. builder.setCacheNames(this.cacheNames);
  129. }
  130. if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) && StringUtils.hasText(this.keyGenerator)) {
  131. builder.setKeyGenerator(this.keyGenerator);
  132. }
  133. if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
  134. // One of these is set so we should not inherit anything
  135. } else if (StringUtils.hasText(this.cacheResolver)) {
  136. builder.setCacheResolver(this.cacheResolver);
  137. } else if (StringUtils.hasText(this.cacheManager)) {
  138. builder.setCacheManager(this.cacheManager);
  139. }
  140. }
  141. }
  142. }

经过这一番解析后,三大缓存注解,最终都被收集到CacheOperation里来了,这也就和CacheOperationSource缓存属性源接口的功能照应了起来。

CacheOperationInvocationContext:执行上下文

它代表缓存执行时的上下文。

  1. //@since 4.1 注意泛型O extends BasicOperation
  2. public interface CacheOperationInvocationContext<O extends BasicOperation> {
  3. // 缓存操作属性CacheOperation
  4. O getOperation();
  5. // 目标类、目标方法
  6. Object getTarget();
  7. Method getMethod();
  8. // 方法入参们
  9. Object[] getArgs();
  10. }

它只有一个CacheAspectSupport内部类实现CacheOperationContext,此处也搬上来吧:

  1. protected class CacheOperationContext implements CacheOperationInvocationContext<CacheOperation> {
  2. // 它里面包含了CacheOperation、Method、Class、Method targetMethod;(注意有两个Method)、AnnotatedElementKey、KeyGenerator、CacheResolver等属性
  3. // this.method = BridgeMethodResolver.findBridgedMethod(method);
  4. // this.targetMethod = (!Proxy.isProxyClass(targetClass) ? AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
  5. // this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
  6. private final CacheOperationMetadata metadata;
  7. private final Object[] args;
  8. private final Object target;
  9. private final Collection<? extends Cache> caches;
  10. private final Collection<String> cacheNames;
  11. @Nullable
  12. private Boolean conditionPassing; // 标志CacheOperation.conditon是否是true:表示通过 false表示未通过
  13. public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
  14. this.metadata = metadata;
  15. this.args = extractArgs(metadata.method, args);
  16. this.target = target;
  17. // 这里方法里调用了cacheResolver.resolveCaches(context)方法来得到缓存们~~~~ CacheResolver
  18. this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
  19. // 从caches拿到他们的names们
  20. this.cacheNames = createCacheNames(this.caches);
  21. }
  22. ... // 省略get/set
  23. protected boolean isConditionPassing(@Nullable Object result) {
  24. if (this.conditionPassing == null) {
  25. // 如果配置了并且还没被解析过,此处就解析condition条件~~~
  26. if (StringUtils.hasText(this.metadata.operation.getCondition())) {
  27. // 执行上下文:此处就不得不提一个非常重要的它了:CacheOperationExpressionEvaluator
  28. // 它代表着缓存操作中SpEL的执行上下文~~~ 具体可以先参与下面的对它的介绍
  29. // 解析conditon~
  30. EvaluationContext evaluationContext = createEvaluationContext(result);
  31. this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
  32. this.metadata.methodKey, evaluationContext);
  33. } else {
  34. this.conditionPassing = true;
  35. }
  36. }
  37. return this.conditionPassing;
  38. }
  39. // 解析CacheableOperation和CachePutOperation好的unless
  40. protected boolean canPutToCache(@Nullable Object value) {
  41. String unless = "";
  42. if (this.metadata.operation instanceof CacheableOperation) {
  43. unless = ((CacheableOperation) this.metadata.operation).getUnless();
  44. } else if (this.metadata.operation instanceof CachePutOperation) {
  45. unless = ((CachePutOperation) this.metadata.operation).getUnless();
  46. }
  47. if (StringUtils.hasText(unless)) {
  48. EvaluationContext evaluationContext = createEvaluationContext(value);
  49. return !evaluator.unless(unless, this.metadata.methodKey, evaluationContext);
  50. }
  51. return true;
  52. }
  53. // 这里注意:生成key 需要注意步骤。
  54. // 若配置了key(非空串):那就作为SpEL解析出来
  55. // 否则走keyGenerator去生成~~~(所以你会发现,即使咱们没有配置key和keyGenerator,程序依旧能正常work,只是生成的key很长而已~~~)
  56. // (keyGenerator你可以能没配置????)
  57. // 若你自己没有手动指定过KeyGenerator,那会使用默认的SimpleKeyGenerator 它的实现比较简单
  58. // 其实若我们自定义KeyGenerator,我觉得可以继承自`SimpleKeyGenerator `,而不是直接实现接口~~~
  59. @Nullable
  60. protected Object generateKey(@Nullable Object result) {
  61. if (StringUtils.hasText(this.metadata.operation.getKey())) {
  62. EvaluationContext evaluationContext = createEvaluationContext(result);
  63. return evaluator.key(this.metadata.operation.getKey(), this.metadata.methodKey, evaluationContext);
  64. }
  65. // key的优先级第一位,没有指定采用生成器去生成~
  66. return this.metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
  67. }
  68. private EvaluationContext createEvaluationContext(@Nullable Object result) {
  69. return evaluator.createEvaluationContext(this.caches, this.metadata.method, this.args,
  70. this.target, this.metadata.targetClass, this.metadata.targetMethod, result, beanFactory);
  71. }
  72. ...
  73. }

CacheOperationExpressionEvaluator:缓存操作执行器

在这之前,在讲解发布订阅、事件机制的文章中:
【小家Spring】从Spring中的(ApplicationEvent)事件驱动机制出发,聊聊【观察者模式】【监听者模式】【发布订阅模式】【消息队列MQ】【EventSourcing】…
讲到过EventExpressionEvaluator,它在解析@EventListener注解的condition属性的时候被使用到,它也继承自抽象父类CachedExpressionEvaluator
在这里插入图片描述
可见本类也是抽象父类的一个实现,是不是顿时有种熟悉感了?

  1. // @since 3.1 注意抽象父类CachedExpressionEvaluator在Spring4.2才有
  2. // CachedExpressionEvaluator里默认使用的解析器是:SpelExpressionParser 以及
  3. // 还准备了一个ParameterNameDiscoverer 可以交给执行上文CacheEvaluationContext使用
  4. class CacheOperationExpressionEvaluator extends CachedExpressionEvaluator {
  5. // 注意这两个属性是public的 在CacheAspectSupport里都有使用~~~
  6. public static final Object NO_RESULT = new Object();
  7. public static final Object RESULT_UNAVAILABLE = new Object();
  8. public static final String RESULT_VARIABLE = "result";
  9. private final Map<ExpressionKey, Expression> keyCache = new ConcurrentHashMap<>(64);
  10. private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);
  11. private final Map<ExpressionKey, Expression> unlessCache = new ConcurrentHashMap<>(64);
  12. // 这个方法是创建执行上下文。能给解释:为何可以使用#result这个key的原因
  13. // 此方法的入参确实不少:8个
  14. public EvaluationContext createEvaluationContext(Collection<? extends Cache> caches,
  15. Method method, Object[] args, Object target, Class<?> targetClass, Method targetMethod,
  16. @Nullable Object result, @Nullable BeanFactory beanFactory) {
  17. // root对象,此对象的属性决定了你的#root能够取值的范围,具体下面有贴出此类~
  18. CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches, method, args, target, targetClass);
  19. // 它继承自MethodBasedEvaluationContext,已经讲解过了,本文就不继续深究了~
  20. CacheEvaluationContext evaluationContext = new CacheEvaluationContext(rootObject, targetMethod, args, getParameterNameDiscoverer());
  21. if (result == RESULT_UNAVAILABLE) {
  22. evaluationContext.addUnavailableVariable(RESULT_VARIABLE);
  23. } else if (result != NO_RESULT) {
  24. // 这一句话就是为何我们可以使用#result的核心原因~
  25. evaluationContext.setVariable(RESULT_VARIABLE, result);
  26. }
  27. // 从这里可知,缓存注解里也是可以使用容器内的Bean的~
  28. if (beanFactory != null) {
  29. evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
  30. }
  31. return evaluationContext;
  32. }
  33. // 都有缓存效果哦,因为都继承自抽象父类CachedExpressionEvaluator嘛
  34. // 抽象父类@since 4.2才出来,就是给解析过程提供了缓存的效果~~~~(注意此缓存非彼缓存)
  35. // 解析key的SpEL表达式
  36. @Nullable
  37. public Object key(String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
  38. return getExpression(this.keyCache, methodKey, keyExpression).getValue(evalContext);
  39. }
  40. // condition和unless的解析结果若不是bool类型,统一按照false处理~~~~
  41. public boolean condition(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
  42. return (Boolean.TRUE.equals(getExpression(this.conditionCache, methodKey, conditionExpression).getValue(evalContext, Boolean.class)));
  43. }
  44. public boolean unless(String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
  45. return (Boolean.TRUE.equals(getExpression(this.unlessCache, methodKey, unlessExpression).getValue(evalContext, Boolean.class)));
  46. }
  47. /** * Clear all caches. */
  48. void clear() {
  49. this.keyCache.clear();
  50. this.conditionCache.clear();
  51. this.unlessCache.clear();
  52. }
  53. }
  54. // #root可取的值,参考CacheExpressionRootObject这个对象的属性
  55. // @since 3.1
  56. class CacheExpressionRootObject {
  57. // 可见#root.caches竟然都是阔仪的~~~
  58. private final Collection<? extends Cache> caches;
  59. private final Method method;
  60. private final Object[] args;
  61. private final Object target;
  62. private final Class<?> targetClass;
  63. // 省略所有的get/set(其实只有get方法)
  64. }

缓存操作的执行器,能让你深刻的理解到#root#result的使用,并且永远忘记不了了。

CacheResolver

其名字已经暗示了其是Cache解析器,用于根据实际情况来动态解析使用哪个Cache,它是Spring4.1提供的新特性。

  1. // @since 4.1
  2. @FunctionalInterface
  3. public interface CacheResolver {
  4. // 根据执行上下文,拿到最终的缓存Cache集合
  5. // CacheOperationInvocationContext:执行上下文
  6. Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
  7. }

它有一些内置实现如下:
在这里插入图片描述

AbstractCacheResolver

具体实现根据调用上下文提供缓存名称集合。

  1. // @since 4.1 实现了InitializingBean
  2. public abstract class AbstractCacheResolver implements CacheResolver, InitializingBean {
  3. // 课件它还是依赖于CacheManager的
  4. @Nullable
  5. private CacheManager cacheManager;
  6. // 构造函数统一protected
  7. protected AbstractCacheResolver() {
  8. }
  9. protected AbstractCacheResolver(CacheManager cacheManager) {
  10. this.cacheManager = cacheManager;
  11. }
  12. // 设置,指定一个CacheManager
  13. public void setCacheManager(CacheManager cacheManager) {
  14. this.cacheManager = cacheManager;
  15. }
  16. public CacheManager getCacheManager() {
  17. Assert.state(this.cacheManager != null, "No CacheManager set");
  18. return this.cacheManager;
  19. }
  20. // 做了一步校验而已~~~CacheManager 必须存在
  21. // 这是一个使用技巧哦 自己的在设计框架的框架的时候可以使用~
  22. @Override
  23. public void afterPropertiesSet() {
  24. Assert.notNull(this.cacheManager, "CacheManager is required");
  25. }
  26. @Override
  27. public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
  28. // getCacheNames抽象方法,子类去实现~~~~
  29. Collection<String> cacheNames = getCacheNames(context);
  30. if (cacheNames == null) {
  31. return Collections.emptyList();
  32. }
  33. // 根据cacheNames 去CacheManager里面拿到Cache对象, 作为最终的返回
  34. Collection<Cache> result = new ArrayList<>(cacheNames.size());
  35. for (String cacheName : cacheNames) {
  36. Cache cache = getCacheManager().getCache(cacheName);
  37. if (cache == null) {
  38. throw new IllegalArgumentException("Cannot find cache named '" +
  39. cacheName + "' for " + context.getOperation());
  40. }
  41. result.add(cache);
  42. }
  43. return result;
  44. }
  45. // 子类需要实现此抽象方法
  46. @Nullable
  47. protected abstract Collection<String> getCacheNames(CacheOperationInvocationContext<?> context);
  48. }

此抽象类一样的,只是模版实现了resolveCaches()方法。抽象方法的实现交给了实现类

SimpleCacheResolver
  1. public class SimpleCacheResolver extends AbstractCacheResolver {
  2. ...
  3. @Override
  4. protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
  5. return context.getOperation().getCacheNames();
  6. }
  7. ...
  8. }

这个实现太简单了,没啥好说的。

NamedCacheResolver
  1. public class NamedCacheResolver extends AbstractCacheResolver {
  2. @Nullable
  3. private Collection<String> cacheNames;
  4. public NamedCacheResolver() {
  5. }
  6. public NamedCacheResolver(CacheManager cacheManager, String... cacheNames) {
  7. super(cacheManager);
  8. this.cacheNames = new ArrayList<>(Arrays.asList(cacheNames));
  9. }
  10. /** * Set the cache name(s) that this resolver should use. */
  11. public void setCacheNames(Collection<String> cacheNames) {
  12. this.cacheNames = cacheNames;
  13. }
  14. @Override
  15. protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
  16. return this.cacheNames;
  17. }
  18. }

此解析器的特点是,可以自己setCacheNames()

若内建的不符合条件,我们可以自己实现一个自己的CacheResolver。比如实现和业务相关的缓存处理器(若Class==某Class,做些特殊的操作之类的)

需要注意的是:即使你配置使用的是CacheResolver,你也必须在配置里提供cacheNames至少一个的,因为毕竟是根据你配置的CacheNames去CacheManager里找(当然,若是你的自定义实现除外

CacheOperationSourcePointcut

Pointcut小伙伴应该不陌生,在AOP章节重点又重点的描述过,我们知道Pointcut直接关乎到是否要生成代理对象,所以此类还是蛮重要的。

  1. // @since 3.1 它是个StaticMethodMatcherPointcut 所以方法入参它不关心
  2. abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
  3. // 如果你这个类就是一个CacheManager,不切入
  4. @Override
  5. public boolean matches(Method method, Class<?> targetClass) {
  6. if (CacheManager.class.isAssignableFrom(targetClass)) {
  7. return false;
  8. }
  9. // 获取到当前的缓存属性源~~~getCacheOperationSource()是个抽象方法
  10. CacheOperationSource cas = getCacheOperationSource();
  11. // 下面一句话解释为:如果方法/类上标注有缓存相关的注解,就切入进取~~
  12. // 具体逻辑请参见方法:cas.getCacheOperations();
  13. return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
  14. }
  15. @Override
  16. public boolean equals(Object other) {
  17. if (this == other) {
  18. return true;
  19. }
  20. if (!(other instanceof CacheOperationSourcePointcut)) {
  21. return false;
  22. }
  23. CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
  24. return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
  25. }
  26. @Override
  27. public int hashCode() {
  28. return CacheOperationSourcePointcut.class.hashCode();
  29. }
  30. @Override
  31. public String toString() {
  32. return getClass().getName() + ": " + getCacheOperationSource();
  33. }
  34. /** * Obtain the underlying {@link CacheOperationSource} (may be {@code null}). * To be implemented by subclasses. */
  35. @Nullable
  36. protected abstract CacheOperationSource getCacheOperationSource();
  37. }

CacheErrorHandler

处理缓存发生错误时的策略接口。在大多数情况下,提供者抛出的任何异常都应该简单地被抛出到客户机上,但是在某些情况下,基础结构可能需要以不同的方式处理缓存提供者异常。这个时候可以使用此接口来处理

接口内容十分之简单:

  1. public interface CacheErrorHandler {
  2. void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
  3. void handleCachePutError(RuntimeException exception, Cache cache, Object key, @Nullable Object value);
  4. void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
  5. void handleCacheClearError(RuntimeException exception, Cache cache);
  6. }

Spring对它唯一内建实现为SimpleCacheErrorHandler,代码我不贴了,全是空实现,所以它是一个Adapter适配器形式的存在。
若你想自己提供CacheErrorHandler,你可以继承自SimpleCacheErrorHandler来弄~~~

AbstractCacheInvoker

它的作用是在进行缓存操作时发生异常时,调用组件CacheErrorHandler来进行处理~

  1. // @since 4.1
  2. public abstract class AbstractCacheInvoker {
  3. //protected属性~
  4. protected SingletonSupplier<CacheErrorHandler> errorHandler;
  5. protected AbstractCacheInvoker() {
  6. this.errorHandler = SingletonSupplier.of(SimpleCacheErrorHandler::new);
  7. }
  8. protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
  9. this.errorHandler = SingletonSupplier.of(errorHandler);
  10. }
  11. // 此处用的obtain方法 它能保证不为null
  12. public CacheErrorHandler getErrorHandler() {
  13. return this.errorHandler.obtain();
  14. }
  15. @Nullable
  16. protected Cache.ValueWrapper doGet(Cache cache, Object key) {
  17. try {
  18. return cache.get(key);
  19. } catch (RuntimeException ex) {
  20. getErrorHandler().handleCacheGetError(ex, cache, key);
  21. // 只有它有返回值哦 返回null
  22. return null; // If the exception is handled, return a cache miss
  23. }
  24. }
  25. ... // 省略doPut、doEvict、doClear 处理方式同上
  26. }

可见这个类在Spring4.1提出,专门用于处理异常的,毕竟CacheErrorHandler也是Spring4.1后才有。

总结

本篇文章为讲解缓存注解的深入原理分析进行铺垫,所以密切关注这篇文章:
【小家Spring】玩转Spring Cache — @Cacheable/@CachePut/@CacheEvict注解的使用以及原理深度剖析


关注A哥






































Author A哥(YourBatman)
个人站点 www.yourbatman.cn
E-mail yourbatman@qq.com
微 信 fsx641385712
活跃平台
公众号 BAT的乌托邦(ID:BAT-utopia)
知识星球 BAT的乌托邦
每日文章推荐 每日文章推荐

BAT的乌托邦

发表评论

表情:
评论列表 (有 0 条评论,443人围观)

还没有评论,来说两句吧...

相关阅读