spring cache注解驱动
spring cache注解驱动
概述
省流
利用spring aop。
拦截逻辑:类或者方法上有相应注解。
增强逻辑:解析注解元信息执行相应操作。
核心注解
@EnableCaching
作用:开启Spring注解驱动的Cache支持。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
/**
* 指示是否要创建基于子类的 (CGLIB) 代理,而不是
* 到基于标准 Java 接口的代理。默认值为 {@code false}。
*/
boolean proxyTargetClass() default false;
/**
* 如何应用缓存建议(Advised)。
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* 缓存顾问(Advisor)的执行顺序
* 当在特定连接点应用多个建议(Advised)时。
* <p>默认值为 {@link Ordered#LOWEST_PRECEDENCE}。
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}
@CacheConfig
作用:类级别的默认缓存配置。
/**
* {@code @CacheConfig} 提供了一种共享公共缓存相关机制
* class级别的设置。
*
* <p>当这个注解存在于给定的类上时,它提供了一个集合
* 该类中定义的任何缓存操作的默认设置。
*
* @author Stephane Nicoll
* @author Sam Brannen
* @since 4.1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
/**
* 定义的缓存操作要考虑的默认缓存的名称
*/
String[] cacheNames() default {};
/**
* 默认 {@link org.springframework.cache.interceptor.KeyGenerator} 的 bean 名称
*/
String keyGenerator() default "";
/**
* 自定义{@link org.springframework.cache.CacheManager}的bean名称,用于
* 创建一个默认的{@link org.springframework.cache.interceptor.CacheResolver} 如果没有设置。
* @see org.springframework.cache.interceptor.SimpleCacheResolver
*/
String cacheManager() default "";
/**
* 要使用的自定义 {@link org.springframework.cache.interceptor.CacheResolver} 的 bean 名称。
*/
String cacheResolver() default "";
}
@Cacheable
作用:缓存调用方法的结果,仅在不存在相应缓存时执行目标方法。
/**
* 缓存调用方法的结果。
*
* <p>每次调用建议(Advised)的方法时,都会应用缓存行为,
* 检查是否已经为给定的参数调用过该方法。
* 默认值只是使用方法参数来计算密钥,但
* SpEL 表达式可以通过 {@link #key} 属性或自定义
* {@link org.springframework.cache.interceptor.KeyGenerator} 实现可以
* 替换默认的(参见 {@link #keyGenerator})。
*
* <p>如果在计算键的缓存中找不到值,则目标方法
* 将被调用,返回的值将存储在关联的缓存中。
* 请注意,{@link java.util.Optional} 返回类型是自动解包的。
* 如果 {@code Optional} 值为 {@linkplain java.util.Optional#isPresent()
* present},它将存储在关联的缓存中。如果 {@code Optional}
* value 不存在,{@code null} 将存储在关联的缓存中。
*
* <p>此注解可用作<em>元注</em>解来创建自定义
* 带有属性覆盖的<em>组合注释</em>。
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 3.1
* @see CacheConfig
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
/**
* Alias for {@link #cacheNames}.
*/
@AliasFor("cacheNames")
String[] value() default {};
/**
* 存储方法调用结果的缓存的名称。
* <p>名称可用于确定目标缓存(或多个缓存),匹配
* 特定 Bean 定义的限定符值或 Bean 名称。
* @since 4.2
* @see #value
* @see CacheConfig#cacheNames
*/
@AliasFor("value")
String[] cacheNames() default {};
/**
* Spring 表达式语言 (SpEL) 表达式,用于动态计算密钥。
* <p>默认值为 {@code “”},表示所有方法参数都被视为一个键,
* 除非已配置自定义 {@link #keyGenerator}。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
*/
String key() default "";
/**
* 自定义 {@link org.springframework.cache.interceptor.KeyGenerator} 的 Bean 名称
* 使用。
* <p>与 {@link #key} 属性互斥。
* @see CacheConfig#keyGenerator
*/
String keyGenerator() default "";
/**
* 自定义{@link org.springframework.cache.CacheManager}的bean名称,用于
* 创建一个默认的{@link org.springframework.cache.interceptor.CacheResolver} 如果没有设置。
* <p>与 {@link #cacheResolver} 属性互斥。
* @see org.springframework.cache.interceptor.SimpleCacheResolver
* @see CacheConfig#cacheManager
*/
String cacheManager() default "";
/**
* 自定义 {@link org.springframework.cache.interceptor.CacheResolver} 的 Bean 名称
* 使用。
* @see CacheConfig#cacheResolver
*/
String cacheResolver() default "";
/**
* 缓存是有条件的。
* <p>默认值为 {@code “”},表示始终缓存方法结果。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
*/
String condition() default "";
/**
* 表达式用于否决方法缓存。
* <p>与 {@link #condition} 不同,此表达式在方法之后计算
* 已被调用,因此可以引用 {@code result}。
* <p>默认值为 {@code “”},表示缓存永远不会被否决。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* <li>{@code #result} 表示对方法调用结果的引用。为
* 支持的包装器如 {@code Optional}、{@code #result} 是指实际的
* 对象,而不是包装器</li>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
* @since 3.2
*/
String unless() default "";
/**
* 如果多个线程
* 尝试加载同一键的值。同步导致
* 有几个限制:
* <ol>
* <li>不支持 {@link #unless()}</li>
* <li>只能指定一个缓存</li>
* <li>不能组合其他与缓存相关的操作</li>
* </ol>
* @since 4.3
* @see org.springframework.cache.Cache#get(Object, Callable)
*/
boolean sync() default false;
}
@CachePut
作用:缓存方法返回值。
/**
* 方法(或类上的所有方法)触发
* {@link org.springframework.cache.Cache#put(Object, Object) cache put} 操作。
*
* <p>与 {@link Cacheable @Cacheable} 注解相比,此注解
* 不会导致跳过建议(Advised)的方法。相反,它总是会导致
* 方法,其结果将存储在关联的缓存中,如果
* {@link #condition()} 和 {@link #unless()} 表达式相应匹配。注意
* Java8 的 {@code Optional} 返回类型被自动处理,其
* 内容存储在缓存中(如果存在)。
*
* <p>此注解可用作<em>元注</em>解来创建自定义
* 带有属性覆盖的<em>组合注释</em>。
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 3.1
* @see CacheConfig
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
/**
* Alias for {@link #cacheNames}.
*/
@AliasFor("cacheNames")
String[] value() default {};
/**
* 用于缓存放置操作的缓存的名称。
* <p>名称可用于确定目标缓存(或多个缓存),匹配
* 特定 Bean 定义的限定符值或 Bean 名称。
* @since 4.2
* @see #value
* @see CacheConfig#cacheNames
*/
@AliasFor("value")
String[] cacheNames() default {};
/**
* Spring 表达式语言 (SpEL) 表达式,用于动态计算密钥。
* <p>默认值为 {@code “”},表示所有方法参数都被视为一个键,
* 除非设置了自定义 {@link #keyGenerator}。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* <li>{@code #result} 表示对方法调用结果的引用。为
* 支持的包装器如 {@code Optional}、{@code #result} 是指实际的
* 对象,而不是包装器</li>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
*/
String key() default "";
/**
* 自定义 {@link org.springframework.cache.interceptor.KeyGenerator} 的 Bean 名称
* 使用。
* <p>与 {@link #key} 属性互斥。
* @see CacheConfig#keyGenerator
*/
String keyGenerator() default "";
/**
* 自定义{@link org.springframework.cache.CacheManager}的bean名称,用于
* 创建一个默认的{@link org.springframework.cache.interceptor.CacheResolver} 如果没有
* 已设置。
* <p>与 {@link #cacheResolver} 属性互斥。
* @see org.springframework.cache.interceptor.SimpleCacheResolver
* @see CacheConfig#cacheManager
*/
String cacheManager() default "";
/**
* 自定义 {@link org.springframework.cache.interceptor.CacheResolver} 的 Bean 名称
* 使用。
* @see CacheConfig#cacheResolver
*/
String cacheResolver() default "";
/**
* 用于创建缓存的 Spring 表达式语言 (SpEL) 表达式
* 把操作有条件。
* <p>此表达式在调用方法后计算,因为
* PUT 操作的性质,因此可以参考 {@code result}。
* <p>默认值为 {@code “”},表示始终缓存方法结果。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* <li>{@code #result} 表示对方法调用结果的引用。为
* 支持的包装器如 {@code Optional}、{@code #result} 是指实际的
* 对象,而不是包装器</li>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
*/
String condition() default "";
/**
* Spring 表达式语言 (SpEL) 表达式用于否决缓存放置操作。
* <p>默认值为 {@code “”},表示缓存永远不会被否决。
* <p>SpEL 表达式根据提供
* 以下元数据:
* <ul>
* <li>{@code #result} 表示对方法调用结果的引用。为
* 支持的包装器如 {@code Optional}、{@code #result} 是指实际的
* 对象,而不是包装器</li>
* {<li>@code #root.method}、{@code #root.target} 和 {@code #root.caches}
* 对 {@link java.lang.reflect.Method method}、目标对象和
* 分别受影响的缓存。</li>
* <li>方法名称 ({@code #root.methodName}) 和目标类的快捷方式
* ({@code #root.targetClass}) 也可用。
* <li>方法参数可以通过索引访问。例如,第二个参数
* 可以通过 {@code #root.args[1]}、{@code #p1} 或 {@code #a1} 访问。参数
* 如果该信息可用,也可以按名称访问。</li>
* </ul>
* @since 3.2
*/
String unless() default "";
}
CacheEvict
作用:清除缓存。
/**
* 指示一个方法(或一个类上的所有方法)触发
* {@link org.springframework.cache.Cache#evict(Object) cache evict} 操作。
*
* @author Costin Leau
* @author Stephane Nicoll
* @author Sam Brannen
* @since 3.1
* @see CacheConfig
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
/**
* 是否删除缓存中的所有条目。
* <p>默认情况下,仅删除关联键下的值。
* <p>请注意,将此参数设置为 {@code true} 并指定
* {@link #key} 是不允许的。
*/
boolean allEntries() default false;
/**
* 是否应在调用方法之前进行逐出。
* <p>将此属性设置为 {@code true},将导致逐出
* 无论方法结果如何(即,它是否抛出
* 例外与否)。
* <p>默认为 {@code false},表示缓存逐出操作
* 将在成功调用建议的方法<em>后</em>发生(即
* 仅当调用未引发异常时)。
*/
boolean beforeInvocation() default false;
}
@Caching
作用:组合多个上述注解(不同或相同类型)。
/**
* 多个缓存批注(不同或相同类型)的组批注。
*
* <p>此注解可用作<em>元注</em>解来创建自定义
* 带有属性覆盖的<em>组合注释</em>。
*
* @author Costin Leau
* @author Chris Beams
* @since 3.1
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
核心类
AnnotationCacheOperationSource
作用:解析方法或者类上缓存相关的注解。
/**
* {@link CacheInterceptor} 使用的接口。实现知道如何获取
* 缓存操作属性,无论是来自配置,还是元数据属性源级别,或其他地方。
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
*/
public interface CacheOperationSource {
/**
* 确定给定类是否为缓存操作的候选项
* 采用此 {@code CacheOperationSource} 的元数据格式。
* <p>如果此方法返回 {@code false},则给定类上的方法
* 不会因 {@link #getCacheOperations} 内省而被遍历。
* 因此,返回 {@code false} 是对不受影响的优化
* ,而 {@code true} 仅表示该类需要获取
* 对给定类上的每个方法分别进行完全内省。
* @param targetClass 要自省的类
* @return {@code false} 如果已知该类没有缓存操作
* 类或方法级别的元数据;{@code true} 否则。默认
* 实现返回 {@code true},导致定期内省。
* @since 5.2
*/
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
/**
* 返回此方法的缓存操作集合,
* 或 {@code null}(如果该方法不包含<em>可缓存</em>的注释)。
* @param method 内省的方法
* @param targetClass 目标类(可能是 {@code null},在这种情况下
* 必须使用方法的声明类)
* @return 此方法的所有缓存操作,如果未找到,则为 {@code null}
*/
@Nullable
Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass);
}
/**
* 缓存属性的 {@link CacheOperation} 的抽象实现
* 对于方法和实施回退策略:
* 1.特定目标方法;
* 2.目标类;
* 3.申报方式;
* 4. 声明类/接口。
*
* <p>默认使用目标类的缓存属性,如果没有
* 与目标方法相关联。与 关联的任何缓存属性
* Target 方法完全覆盖类缓存属性。
* 如果在目标类上找不到,则调用方法的接口
* 已被调用(如果是 JDK 代理)将被检查。
*
* <p>此实现在属性第一个之后按方法缓存属性
* 使用。如果希望允许动态更改可缓存
* 属性(这不太可能),缓存可以被配置。
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 3.1
*/
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
protected final Log logger = LogFactory.getLog(getClass());
/**
* CacheOperations 的缓存,按特定目标类上的方法进行键控。
* <p>由于此基类未标记为 Serializable,因此将重新创建缓存
* 序列化后 - 前提是具体子类是 Serializable。
*/
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024);
/**
* 确定此方法调用的缓存属性。
* <p>如果未找到 method 属性,则默认为类的 caching 属性。
*
* @param method 当前调用的方法(从不 {@code null})
* @param targetClass 此调用的目标类(可能是 {@code null})
* @return {@link CacheOperation} 表示此方法,如果该方法为 {@code null},则为 { null}
* 不可缓存
*/
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
// Object的方法 就返回null
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
// 有缓存
if (cached != null) {
return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
} else {
/**
* 先找方法,找不到,再从类上找 @Caching、@Cacheable、@CacheEvict、@CachePut
* */
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 {
// 缓存空值
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
}
return cacheOps;
}
}
/**
* Determine a cache key for the given method and target class.
* <p>Must not produce same key for overloaded methods.
* Must produce same key for different instances of the same method.
*
* @param method the method (never {@code null})
* @param targetClass the target class (may be {@code null})
* @return the cache key (never {@code null})
*/
protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
return new MethodClassKey(method, targetClass);
}
@Nullable
private Collection<CacheOperation> computeCacheOperations(Method method, @Nullable Class<?> targetClass) {
// 方法不是public 就返回null
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 如果 targetClass 是代理类,就返回被代理的方法
// 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.
opDef = findCacheOperations(specificMethod.getDeclaringClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
// 不相等,说明有代理类和代理方法
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;
}
/**
* 子类需要实现这一点才能返回给定的class,如果有的话。
*
* @param clazz 类以检索 的属性
* @return 与此类关联的所有缓存属性,如果没有,则为 {@code null}
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
/**
* 子类需要实现这一点才能返回给定方法(如果有)。
*
* @param method 检索属性的方法
* @return 与此方法关联的所有缓存属性,如果没有,则为 {@code null}
*/
@Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
/**
* 是否应该只允许公共方法具有缓存语义?
* <p>默认实现返回 {@code false}。
*/
protected boolean allowPublicMethodsOnly() {
return false;
}
}
/**
* {@link org.springframework.cache.interceptor.CacheOperationSource 的实现
* CacheOperationSource} 接口,用于处理以注释格式缓存元数据。
*
* <p>此类读取 Spring 的 {@link Cacheable}、{@link CachePut} 和 {@link CacheEvict}
* 注解,并将相应的缓存操作定义暴露给 Spring 的缓存
* 基础设施。此类还可以用作自定义项的基类
* {@code CacheOperationSource}。
*
* @author Costin Leau
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 3.1
*/
@SuppressWarnings("serial")
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
private final boolean publicMethodsOnly;
private final Set<CacheAnnotationParser> annotationParsers;
/**
* 依赖于CacheAnnotationParser 默认有SpringCacheAnnotationParser的实现
* @param targetClass 要自省的类
*/
@Override
public boolean isCandidateClass(Class<?> targetClass) {
for (CacheAnnotationParser parser : this.annotationParsers) {
if (parser.isCandidateClass(targetClass)) {
return true;
}
}
return false;
}
@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));
}
/**
* 确定给定 {@link CacheOperationProvider} 的缓存操作。
* <p>此实现委托给已配置
* {@link CacheAnnotationParser CacheAnnotationParsers}
* 用于将已知注解解析到 Spring 的元数据属性类中。
* <p>可以覆盖以支持带有缓存元数据的自定义注释。
* @param provider 要使用的缓存操作提供程序
* @return 配置的缓存操作,如果没有找到,则 {@code null}
*/
@Nullable
protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
// 类或者方法 解析出来的 CacheOperation
Collection<CacheOperation> ops = null;
// 默认就一个parser ---> SpringCacheAnnotationParser
for (CacheAnnotationParser parser : this.annotationParsers) {
/**
* getCacheOperations 是执行这个
* {@link SpringCacheAnnotationParser#parseCacheAnnotations(Method)}
* {@link SpringCacheAnnotationParser#parseCacheAnnotations(Class)}
*
* 逻辑很简单,从方法或者类上 找 @Cacheable、@CacheEvict、@CachePut、@Caching 每个都会解析成 CacheOperation 对象,
* 比如:
* - 方法:方法有匹配的注解、方法的接口方法有匹配的注解、父类方法有匹配的注解 已最小维度为准,返回的是方法上匹配的注解
* - 类:类有匹配的注解、类的父类有匹配的注解、类的接口有匹配的注解 已最小维度为准,返回的是类上匹配的注解
* */
Collection<CacheOperation> annOps = provider.getCacheOperations(parser);
if (annOps != null) {
if (ops == null) {
ops = annOps;
} else {
// 汇总parser解析的值
Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
combined.addAll(ops);
combined.addAll(annOps);
// 赋值
ops = combined;
}
}
}
return ops;
}
/**
* By default, only public methods can be made cacheable.
*/
@Override
protected boolean allowPublicMethodsOnly() {
return this.publicMethodsOnly;
}
/**
* Callback interface providing {@link CacheOperation} instance(s) based on
* a given {@link CacheAnnotationParser}.
*/
@FunctionalInterface
protected interface CacheOperationProvider {
/**
* Return the {@link CacheOperation} instance(s) provided by the specified parser.
* @param parser the parser to use
* @return the cache operations, or {@code null} if none found
*/
@Nullable
Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser);
}
}
SpringCacheAnnotationParser
作用:缓存注解解析器。
public interface CacheAnnotationParser {
default boolean isCandidateClass(Class<?> targetClass) {
return true;
}
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Class<?> type);
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Method method);
}
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);
}
@Override
public boolean isCandidateClass(Class<?> targetClass) {
return AnnotationUtils.isCandidateClass(targetClass, CACHE_OPERATION_ANNOTATIONS);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
// 这个很关键用来解析类上的 @CacheConfig
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
// 这个很关键用来解析类上的 @CacheConfig
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
// 先递归找
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;
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
/**
* 就是从 ae 上找到 @Cacheable、@CacheEvict、@CachePut、@Caching 这些注解
* */
Collection<? extends Annotation> anns = (localOnly ?
// 找 当前类或者方法上的注解
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
// 找 会递归找,就是 类的父类、接口,方法的接口返、方法的父类方法
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
// 是空
if (anns.isEmpty()) {
return null;
}
// 最终的结果
final Collection<CacheOperation> ops = new ArrayList<>(1);
// 分成几种,分批处理
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)));
// @Caching 就是上面 三个的合并写法,所以这里会传入 ops ,解析 @Caching 其实还是按照三个注解解析 赛道 ops中
anns.stream().filter(ann -> ann instanceof Caching).forEach(
ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops));
return ops;
}
private CacheableOperation parseCacheableAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
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());
// 应用默认值,
defaultConfig.applyDefault(builder);
// 生成 CacheableOperation 实例
CacheableOperation op = builder.build();
// 验证
validateCacheOperation(ae, op);
return op;
}
private CacheEvictOperation parseEvictAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cacheEvict.cacheNames());
builder.setCondition(cacheEvict.condition());
builder.setKey(cacheEvict.key());
builder.setKeyGenerator(cacheEvict.keyGenerator());
builder.setCacheManager(cacheEvict.cacheManager());
builder.setCacheResolver(cacheEvict.cacheResolver());
builder.setCacheWide(cacheEvict.allEntries());
builder.setBeforeInvocation(cacheEvict.beforeInvocation());
defaultConfig.applyDefault(builder);
CacheEvictOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
private CacheOperation parsePutAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
CachePutOperation.Builder builder = new CachePutOperation.Builder();
builder.setName(ae.toString());
builder.setCacheNames(cachePut.cacheNames());
builder.setCondition(cachePut.condition());
builder.setUnless(cachePut.unless());
builder.setKey(cachePut.key());
builder.setKeyGenerator(cachePut.keyGenerator());
builder.setCacheManager(cachePut.cacheManager());
builder.setCacheResolver(cachePut.cacheResolver());
defaultConfig.applyDefault(builder);
CachePutOperation op = builder.build();
validateCacheOperation(ae, op);
return op;
}
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 void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae.toString()
+ "'. Both 'key' and 'keyGenerator' attributes have been set. " +
"These attributes are mutually exclusive: either set the SpEL expression used to"
+
"compute the key at runtime or set the name of the KeyGenerator bean to use.");
}
if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae.toString()
+ "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
"These attributes are mutually exclusive: the cache manager is used to configure a"
+
"default cache resolver if none is set. If a cache resolver is set, the cache manager"
+
"won't be used.");
}
}
/**
* 为给定的一组缓存操作提供默认设置。
*/
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;
}
/**
* 将默认值应用于指定的{@link CacheOperation.Builder}。
* @param builder要更新的操作生成器
*/
public void applyDefault(CacheOperation.Builder builder) {
// 没初始化过
if (!this.initialized) {
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;
}
// 只有当 @Cacheable、@CacheEvict、@CachePut 没有设置值是,才会使用 @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);
}
}
}
}
CacheManager
缓存管理器
/**
* Spring 的中央缓存管理器 SPI。
*
* <p>允许检索命名的 {@link Cache} 区域。
*
* @author Costin Leau
* @author Sam Brannen
* @since 3.1
*/
public interface CacheManager {
/**
* 获取与给定名称关联的缓存。
* @param name 命名缓存标识符(不得为 {@code null})
* @return 关联的缓存,如果此类缓存, 不存在或无法创建则为 {@code null}
*/
@Nullable
Cache getCache(String name);
/**
* 获取此管理器已知的缓存名称的集合。
* @return 缓存管理器已知的所有缓存的名称
*/
Collection<String> getCacheNames();
}
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
private volatile Set<String> cacheNames = Collections.emptySet();
// Early cache initialization on startup
@Override
public void afterPropertiesSet() {
initializeCaches();
}
/**
* Initialize the static configuration of caches.
* <p>Triggered on startup through {@link #afterPropertiesSet()};
* can also be called to re-initialize at runtime.
* @since 4.2.2
* @see #loadCaches()
*/
public void initializeCaches() {
Collection<? extends Cache> caches = loadCaches();
synchronized (this.cacheMap) {
this.cacheNames = Collections.emptySet();
this.cacheMap.clear();
Set<String> cacheNames = new LinkedHashSet<>(caches.size());
for (Cache cache : caches) {
String name = cache.getName();
this.cacheMap.put(name, decorateCache(cache));
cacheNames.add(name);
}
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
}
/**
* Load the initial caches for this cache manager.
* <p>Called by {@link #afterPropertiesSet()} on startup.
* The returned collection may be empty but must not be {@code null}.
*/
protected abstract Collection<? extends Cache> loadCaches();
// Lazy cache initialization on access
@Override
@Nullable
public Cache getCache(String name) {
// Quick check for existing cache...
Cache cache = this.cacheMap.get(name);
if (cache != null) {
return cache;
}
// The provider may support on-demand cache creation...
Cache missingCache = getMissingCache(name);
if (missingCache != null) {
// Fully synchronize now for missing cache registration
synchronized (this.cacheMap) {
cache = this.cacheMap.get(name);
if (cache == null) {
cache = decorateCache(missingCache);
this.cacheMap.put(name, cache);
updateCacheNames(name);
}
}
}
return cache;
}
@Override
public Collection<String> getCacheNames() {
return this.cacheNames;
}
// Common cache initialization delegates for subclasses
/**
* Check for a registered cache of the given name.
* In contrast to {@link #getCache(String)}, this method does not trigger
* the lazy creation of missing caches via {@link #getMissingCache(String)}.
* @param name the cache identifier (must not be {@code null})
* @return the associated Cache instance, or {@code null} if none found
* @since 4.1
* @see #getCache(String)
* @see #getMissingCache(String)
*/
@Nullable
protected final Cache lookupCache(String name) {
return this.cacheMap.get(name);
}
/**
* Dynamically register an additional Cache with this manager.
* @param cache the Cache to register
* @deprecated as of Spring 4.3, in favor of {@link #getMissingCache(String)}
*/
@Deprecated
protected final void addCache(Cache cache) {
String name = cache.getName();
synchronized (this.cacheMap) {
if (this.cacheMap.put(name, decorateCache(cache)) == null) {
updateCacheNames(name);
}
}
}
/**
* Update the exposed {@link #cacheNames} set with the given name.
* <p>This will always be called within a full {@link #cacheMap} lock
* and effectively behaves like a {@code CopyOnWriteArraySet} with
* preserved order but exposed as an unmodifiable reference.
* @param name the name of the cache to be added
*/
private void updateCacheNames(String name) {
Set<String> cacheNames = new LinkedHashSet<>(this.cacheNames);
cacheNames.add(name);
this.cacheNames = Collections.unmodifiableSet(cacheNames);
}
// Overridable template methods for cache initialization
/**
* Decorate the given Cache object if necessary.
* @param cache the Cache object to be added to this CacheManager
* @return the decorated Cache object to be used instead,
* or simply the passed-in Cache object by default
*/
protected Cache decorateCache(Cache cache) {
return cache;
}
/**
* Return a missing cache with the specified {@code name}, or {@code null} if
* such a cache does not exist or could not be created on demand.
* <p>Caches may be lazily created at runtime if the native provider supports it.
* If a lookup by name does not yield any result, an {@code AbstractCacheManager}
* subclass gets a chance to register such a cache at runtime. The returned cache
* will be automatically added to this cache manager.
* @param name the name of the cache to retrieve
* @return the missing cache, or {@code null} if no such cache exists or could be
* created on demand
* @since 4.1
* @see #getCache(String)
*/
@Nullable
protected Cache getMissingCache(String name) {
return null;
}
}
Cache
缓存
public interface Cache {
/**
* 返回缓存名称。
*/
String getName();
/**
*返回基础本机缓存提供程序。
*/
Object getNativeCache();
@Nullable
ValueWrapper get(Object key);
@Nullable
<T> T get(Object key, @Nullable Class<T> type);
@Nullable
<T> T get(Object key, Callable<T> valueLoader);
void put(Object key, @Nullable Object value);
@Nullable
default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
ValueWrapper existingValue = get(key);
if (existingValue == null) {
put(key, value);
}
return existingValue;
}
/**
* 如果存在此键的映射,请从此缓存中逐出该映射。
* <p>实际逐出可以异步执行,也可以延迟执行后续查找可能仍能看到该条目。
* 例如,事务性缓存装饰器可能就是这种情况。
* 使用 {@link #evictIfPresent} 保证立即删除。
* @param key the key whose mapping is to be removed from the cache
* @see #evictIfPresent(Object)
*/
void evict(Object key);
default boolean evictIfPresent(Object key) {
evict(key);
return false;
}
/**
* 通过删除所有映射来清除缓存。
* <p>实际清算可以异步或延迟进行
* 时尚,后续查找可能仍能看到条目。
* 例如,事务性缓存装饰器可能就是这种情况。
* 使用 {@link #invalidate()} 保证立即删除条目。
* @see #invalidate()
*/
void clear();
/**
* 通过删除所有映射使缓存失效,期望所有
* 条目在后续查找时立即不可见。
* @return {@code true} if the cache was known to have mappings before,
* {@code false} if it did not (or if prior presence of entries could
* not be determined)
* @since 5.2
* @see #clear()
*/
default boolean invalidate() {
clear();
return false;
}
/**
* A (wrapper) object representing a cache value.
*/
@FunctionalInterface
interface ValueWrapper {
/**
* Return the actual value in the cache.
*/
@Nullable
Object get();
}
@SuppressWarnings("serial")
class ValueRetrievalException extends RuntimeException {
@Nullable
private final Object key;
public ValueRetrievalException(@Nullable Object key, Callable<?> loader, Throwable ex) {
super(String.format("Value for key '%s' could not be loaded using '%s'", key, loader), ex);
this.key = key;
}
@Nullable
public Object getKey() {
return this.key;
}
}
}
CacheResolver
作用:通过CacheManager获取Cache。
public interface CacheResolver {
/**
* 返回用于指定调用的缓存。
* @param context the context of the particular invocation
* @return the cache(s) to use (never {@code null})
* @throws IllegalStateException if cache resolution failed
*/
Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context);
}
BeanFactoryCacheOperationSourceAdvisor
切面
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
@Nullable
private CacheOperationSource cacheOperationSource;
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
@Override
@Nullable
protected CacheOperationSource getCacheOperationSource() {
return cacheOperationSource;
}
};
/**
* Set the cache operation attribute source which is used to find cache
* attributes. This should usually be identical to the source reference
* set on the cache interceptor itself.
*/
public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
}
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter) {
this.pointcut.setClassFilter(classFilter);
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
}
CacheOperationSourcePointcut
切点
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
protected CacheOperationSourcePointcut() {
setClassFilter(new CacheOperationSourceClassFilter());
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 拿到解析注解的东西
CacheOperationSource cas = getCacheOperationSource();
/**
* 解析到注解 就是true,表示匹配。默认是 AnnotationCacheOperationSource 执行的是其父类方法
* {@link AbstractFallbackCacheOperationSource#getCacheOperations(Method, Class)}
* */
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
}
/**
* Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
* To be implemented by subclasses.
*/
@Nullable
protected abstract CacheOperationSource getCacheOperationSource();
/**
* {@link ClassFilter} that delegates to {@link CacheOperationSource#isCandidateClass}
* for filtering classes whose methods are not worth searching to begin with.
*/
private class CacheOperationSourceClassFilter implements ClassFilter {
@Override
public boolean matches(Class<?> clazz) {
// 是 CacheManager 就不匹配
if (CacheManager.class.isAssignableFrom(clazz)) {
return false;
}
CacheOperationSource cas = getCacheOperationSource();
// 可以理解成都是true
return (cas == null || cas.isCandidateClass(clazz));
}
}
}
CacheInterceptor
增强
这里可以看到,增强逻辑实在父类的execute方法里面。
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
CacheOperationInvoker aopAllianceInvoker = () -> {
try {
// 放行方法
return invocation.proceed();
}
catch (Throwable ex) {
throw new CacheOperationInvoker.ThrowableWrapper(ex);
}
};
Object target = invocation.getThis();
Assert.state(target != null, "Target must not be null");
try {
// 执行
return execute(aopAllianceInvoker, target, method, invocation.getArguments());
}
catch (CacheOperationInvoker.ThrowableWrapper th) {
throw th.getOriginal();
}
}
}
public abstract class CacheAspectSupport extends AbstractCacheInvoker
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
protected final Log logger = LogFactory.getLog(getClass());
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache = new ConcurrentHashMap<>(1024);
private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();
@Nullable
private CacheOperationSource cacheOperationSource;
/**
* 默认是 SimpleKeyGenerator
*/
private SingletonSupplier<KeyGenerator> keyGenerator = SingletonSupplier.of(SimpleKeyGenerator::new);
/**
* 默认是 null
*/
@Nullable
private SingletonSupplier<CacheResolver> cacheResolver;
@Nullable
private BeanFactory beanFactory;
private boolean initialized = false;
public void configure(
@Nullable Supplier<CacheErrorHandler> errorHandler, @Nullable Supplier<KeyGenerator> keyGenerator,
@Nullable Supplier<CacheResolver> cacheResolver, @Nullable Supplier<CacheManager> cacheManager) {
this.errorHandler = new SingletonSupplier<>(errorHandler, SimpleCacheErrorHandler::new);
this.keyGenerator = new SingletonSupplier<>(keyGenerator, SimpleKeyGenerator::new);
this.cacheResolver = new SingletonSupplier<>(cacheResolver,
() -> SimpleCacheResolver.of(SupplierUtils.resolve(cacheManager)));
}
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
Assert.notEmpty(cacheOperationSources, "At least 1 CacheOperationSource needs to be specified");
this.cacheOperationSource = (cacheOperationSources.length > 1 ?
new CompositeCacheOperationSource(cacheOperationSources) : cacheOperationSources[0]);
}
public void setCacheOperationSource(@Nullable CacheOperationSource cacheOperationSource) {
this.cacheOperationSource = cacheOperationSource;
}
@Nullable
public CacheOperationSource getCacheOperationSource() {
return this.cacheOperationSource;
}
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = SingletonSupplier.of(keyGenerator);
}
public KeyGenerator getKeyGenerator() {
return this.keyGenerator.obtain();
}
public void setCacheResolver(@Nullable CacheResolver cacheResolver) {
this.cacheResolver = SingletonSupplier.ofNullable(cacheResolver);
}
@Nullable
public CacheResolver getCacheResolver() {
return SupplierUtils.resolve(this.cacheResolver);
}
public void setCacheManager(CacheManager cacheManager) {
this.cacheResolver = SingletonSupplier.of(new SimpleCacheResolver(cacheManager));
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() {
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSources' property is required: " +
"If there are no cacheable methods, then don't use a cache aspect.");
}
@Override
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect");
try {
setCacheManager(this.beanFactory.getBean(CacheManager.class));
} catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary or declare a specific CacheManager to use.", ex);
} catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.", ex);
}
}
this.initialized = true;
}
protected String methodIdentification(Method method, Class<?> targetClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
return ClassUtils.getQualifiedMethodName(specificMethod);
}
protected Collection<? extends Cache> getCaches(
CacheOperationInvocationContext<CacheOperation> context, CacheResolver cacheResolver) {
// 就是通过注解写的 name 从 CacheManager 中获取 Cache 实例
Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);
// 不能没有
if (caches.isEmpty()) {
throw new IllegalStateException("No cache could be resolved for '" +
context.getOperation() + "' using resolver '" + cacheResolver +
"'. At least one cache should be provided per cache operation.");
}
return caches;
}
protected CacheOperationContext getOperationContext(
CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
/**
* 将 CacheOperation 装饰成 CacheOperationMetadata 对象
* 该对象的关键属性:KeyGenerator、CacheResolver,而CacheResolver里面组合了 CacheManager
*
* 注:实例化 CacheOperationMetadata 会检验容器中是否有 CacheManager
* */
CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
/**
* 装饰成 CacheOperationContext
* 主要是拿到 CacheOperation 对应的 Cache 和 将方法参数铺平(就是存在可变参数,就将可变参数铺平)
* */
return new CacheOperationContext(metadata, args, target);
}
protected CacheOperationMetadata getCacheOperationMetadata(
CacheOperation operation, Method method, Class<?> targetClass) {
// 生成key
CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);
// 读缓存
CacheOperationMetadata metadata = this.metadataCache.get(cacheKey);
if (metadata == null) {
KeyGenerator operationKeyGenerator;
if (StringUtils.hasText(operation.getKeyGenerator())) {
// 从容器中获取 KeyGenerator
operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
} else {
// 获取默认的 默认是 SimpleKeyGenerator
operationKeyGenerator = getKeyGenerator();
}
CacheResolver operationCacheResolver;
if (StringUtils.hasText(operation.getCacheResolver())) {
// 从容器中获取 CacheResolver
operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
} else if (StringUtils.hasText(operation.getCacheManager())) {
// 从容器中获取 CacheManager
CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
// 将 CacheManager 装饰成 SimpleCacheResolver
operationCacheResolver = new SimpleCacheResolver(cacheManager);
} else {
/**
* 啥都没得,就获取默认的。
*
* 通过 CachingConfigurer 类型的bean设置的,如果设置了 cacheResolver 就返回,没有就判断有cacheManager,就装饰成SimpleCacheResolver返回
* {@link CachingConfigurer#cacheResolver()}
* {@link CachingConfigurer#cacheManager()}
*
* Tips:也就是说CachingConfigurer必须得设置 CacheResolver或者CacheManager 其中一个,否则这里拿不到
* */
operationCacheResolver = getCacheResolver();
/**
* 非空校验
*
* 也就是说CachingConfigurer必须得设置 CacheResolver或者CacheManager 其中一个,否则到这一步就报错
* */
Assert.state(operationCacheResolver != null, "No CacheResolver/CacheManager set");
}
metadata = new CacheOperationMetadata(operation, method, targetClass,
operationKeyGenerator, operationCacheResolver);
// 缓存起来
this.metadataCache.put(cacheKey, metadata);
}
return metadata;
}
protected <T> T getBean(String beanName, Class<T> expectedType) {
if (this.beanFactory == null) {
throw new IllegalStateException(
"BeanFactory must be set on cache aspect for " + expectedType.getSimpleName() + " retrieval");
}
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName);
}
/**
* Clear the cached metadata.
*/
protected void clearMetadataCache() {
this.metadataCache.clear();
this.evaluator.clear();
}
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
/**
* 这个bean实例化完就会设置为true
* {@link CacheAspectSupport#afterSingletonsInstantiated()}
* */
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
// 被代理类
Class<?> targetClass = getTargetClass(target);
// 拿到解析缓存注解的工具
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
// 返回的是方法或者类上 @Cacheable、@CacheEvict、@CachePut 的解析结果
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
// 不是空,表示方法上有缓存注解
if (!CollectionUtils.isEmpty(operations)) {
/**
* 将 operations、方法、方法参数、执行方法的对象和执行方法的类型 装饰成 CacheOperationContexts 对象
* 然后 invoke
* */
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
@Nullable
protected Object invokeOperation(CacheOperationInvoker invoker) {
return invoker.invoke();
}
private Class<?> getTargetClass(Object target) {
return AopProxyUtils.ultimateTargetClass(target);
}
@Nullable
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
/**
* 是同步的。就是 @Cacheable(sync = true)
* */
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
// 同步的方法上只允许有一个 缓存注解,所以拿第一个就行了
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
/**
* 条件通过。
* 有设置 @Cacheable(condition = "#root.methodName.startsWith('x')") 就解析SpEL看看结果是true就是通过
* 没有设置condition属性,就直接是true
*
* 很简单,就是将 方法的参数和一些固定参数(method、target、targetClass、args、caches) 构造出 EvaluationContext 然后使用 SpelExpressionParser 解析 SpEL表达式
* {@link CacheOperationContext#isConditionPassing(Object)}
* */
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
/**
* 生成key。
* 如果有设置 @Cacheable(key = "#p1"), SpEL的值+Method+Target生成的key 作为缓存的key
* 没有指定key属性,就根据{@link KeyGenerator#generate(Object, Method, Object...)} 返回值作为缓存的key
* */
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
// 同步的方法上只允许有一个 缓存注解,所以拿第一个Cache实例就行了
Cache cache = context.getCaches().iterator().next();
try {
/**
* handleSynchronizedGet 就是 使用 key从Cache中获取,获取不到就invoke方法获取值,会将方法返回值值存到 Cache 中。
*
* 存入Cache的细节:
* 1. 如果方法返回值是Optional类型的,会 {@link Optional#get()} 存到缓存
* 2. 如果Cache配了序列化器,会将值序列化在存到缓存中,获取的时候也会判断是否有序列化器 有就反序列化值
*
* wrapCacheValue 就是如果方法的返回值是 Optional 类型的,就包装一下
*
* 注:并没有看到具体的同步操作,是不是假的同步呀 能不能同步 还得看具体的 {@link Cache#get(Object)} 如何实现的
* 莫非,所谓的同步就是校验 方法上只能有一个 @Cacheable 注解???
* 看了官方文档,这个同步的实现得看具体的Cache实例 https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-cacheable-synchronized
* */
return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
} catch (Cache.ValueRetrievalException ex) {
// Directly propagate ThrowableWrapper from the invoker,
// or potentially also an IllegalArgumentException etc.
ReflectionUtils.rethrowRuntimeException(ex.getCause());
}
} else {
// 不符合缓存条件,反射调用方法返回
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
/**
* 清空缓存,就看是根据key删除 还是 直接清空整个Cache
* 比如 @CacheEvict(allEntries = true) 就是清空整个缓存
* */
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
/**
* 就是遍历 CacheOperationContext,条件通过的,在遍历 Cache 根据key获取缓存值,找到就返回
* */
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
/**
* 就是没有缓存的值,那就构造 CachePutRequest ,就是用来将 方法返回值 存到 Cache中的
* {@link CachePutRequest#apply(Object)}
*
* 该属性是记录符合条件的 @CachePut + @Cacheable
* */
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new ArrayList<>();
if (cacheHit == null) {
// 收集满足 condition 的 CacheableOperation
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
/**
* 有缓存 且 没有符合条件的的@CachePut
*
* 那就不需要更新,那就直接拿缓存的值就行了
* */
if (cacheHit != null && !hasCachePut(contexts)) {
// 拿到缓存的值
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
// 是否需要装饰为Optional对象
returnValue = wrapCacheValue(method, cacheValue);
} else {
// 放行方法,拿到返回值
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
// 是否需要解构Optional对象,拿到其值
cacheValue = unwrapReturnValue(returnValue);
}
/**
* 收集符合条件的@CachePut
* */
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
/**
* 就是就方法返回值 设置到 Cache 中,此时会进行 unless 的解析判断
* {@link CacheOperationContext#canPutToCache(Object)}
* */
cachePutRequest.apply(cacheValue);
}
// 清空缓存
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
// 返回
return returnValue;
}
@Nullable
private Object handleSynchronizedGet(CacheOperationInvoker invoker, Object key, Cache cache) {
InvocationAwareResult invocationResult = new InvocationAwareResult();
/**
* 这里就看你写的 Cache 实现类的方法咯,主要是对值的序列化处理
* {@link ConcurrentMapCache#get(Object, Callable)}
* */
Object result = cache.get(key, () -> {
// 缓存没有执行
invocationResult.invoked = true;
if (logger.isTraceEnabled()) {
// 打印 没命中缓存的日志
logger.trace("No cache entry for key '" + key + "' in cache " + cache.getName());
}
/**
* 结构返回值,若返回值是Optional类型的,就 {@link Optional#get()}
* */
return unwrapReturnValue(invokeOperation(invoker));
});
if (!invocationResult.invoked && logger.isTraceEnabled()) {
// 打印 命中缓存的日志
logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}
// 返回结果
return result;
}
@Nullable
private Object wrapCacheValue(Method method, @Nullable Object cacheValue) {
if (method.getReturnType() == Optional.class &&
(cacheValue == null || cacheValue.getClass() != Optional.class)) {
return Optional.ofNullable(cacheValue);
}
return cacheValue;
}
@Nullable
private Object unwrapReturnValue(@Nullable Object returnValue) {
return ObjectUtils.unwrapOptional(returnValue);
}
private boolean hasCachePut(CacheOperationContexts contexts) {
// Evaluate the conditions *without* the result object because we don't have it yet...
Collection<CacheOperationContext> cachePutContexts = contexts.get(CachePutOperation.class);
Collection<CacheOperationContext> excluded = new ArrayList<>();
for (CacheOperationContext context : cachePutContexts) {
try {
/**
* 条件不通过,就说明是不需要更新的
*
* 使用这个表示,SpEL不可以使用这个变量,要是用了就报错,但是下面捕获到异常并没有做处理,所以用了 也没事
* {@link CacheEvaluationContext#lookupVariable(String)}
* */
if (!context.isConditionPassing(CacheOperationExpressionEvaluator.RESULT_UNAVAILABLE)) {
// 记录
excluded.add(context);
}
} catch (VariableNotAvailableException ex) {
// Ignoring failure due to missing result, consider the cache put has to proceed
}
}
// 不想等,说明是有需要执行的 CachePutOperation
// Check if all puts have been excluded by condition
return (cachePutContexts.size() != excluded.size());
}
private void processCacheEvicts(
Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
// 遍历所有的 CacheEvictOperation
for (CacheOperationContext context : contexts) {
CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
/**
* CacheEvictOperation 是调用前执行 且 满足条件
* 就是 @CacheEvict(beforeInvocation = true)
* */
if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
// 清空缓存
performCacheEvict(context, operation, result);
}
}
}
private void performCacheEvict(
CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
Object key = null;
// 遍历关联的Cache
for (Cache cache : context.getCaches()) {
/**
* 就是 @CacheEvict(allEntries = true)
* */
if (operation.isCacheWide()) {
logInvalidating(context, operation, null);
// 清空整个 Cache
doClear(cache, operation.isBeforeInvocation());
} else {
if (key == null) {
// 缓存的key
key = generateKey(context, result);
}
logInvalidating(context, operation, key);
// 根据 key 清除 Cache 中存储的值
doEvict(cache, key, operation.isBeforeInvocation());
}
}
}
private void logInvalidating(CacheOperationContext context, CacheEvictOperation operation, @Nullable Object key) {
if (logger.isTraceEnabled()) {
logger.trace("Invalidating " + (key != null ? "cache key [" + key + "]" : "entire cache") +
" for operation " + operation + " on method " + context.metadata.method);
}
}
/**
* 仅查找传递条件的 {@link CacheableOperation} 的缓存项。
* @param contexts the cacheable operations
* @return a {@link Cache.ValueWrapper} holding the cached item,
* or {@code null} if none is found
*/
@Nullable
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
// 就是方法上写了多个 @Cacheable
for (CacheOperationContext context : contexts) {
// 匹配
if (isConditionPassing(context, result)) {
// 生成key
Object key = generateKey(context, result);
/**
* 因为一个 @Cacheable(cacheNames = {"a", "b"}) 是可以写多个 Cache的,所以会遍历所有的 Cache 根据key找,先找到就返回
* */
Cache.ValueWrapper cached = findInCaches(context, key);
// 也就是说 先命中就返回其值
if (cached != null) {
return cached;
} else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
}
/**
* Collect the {@link CachePutRequest} for all {@link CacheOperation} using
* the specified result item.
* @param contexts the contexts to handle
* @param result the result item (never {@code null})
* @param putRequests the collection to update
*/
private void collectPutRequests(Collection<CacheOperationContext> contexts,
@Nullable Object result, Collection<CachePutRequest> putRequests) {
for (CacheOperationContext context : contexts) {
// 条件通过
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
// 构造器 CachePutRequest
putRequests.add(new CachePutRequest(context, key));
}
}
}
@Nullable
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
// 遍历 Cache
for (Cache cache : context.getCaches()) {
// 就是根据 key 获取咯
Cache.ValueWrapper wrapper = doGet(cache, key);
if (wrapper != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}
return wrapper;
}
}
return null;
}
private boolean isConditionPassing(CacheOperationContext context, @Nullable Object result) {
boolean passing = context.isConditionPassing(result);
if (!passing && logger.isTraceEnabled()) {
logger.trace("Cache condition failed on method " + context.metadata.method +
" for operation " + context.metadata.operation);
}
return passing;
}
private Object generateKey(CacheOperationContext context, @Nullable Object result) {
Object key = context.generateKey(result);
if (key == null) {
throw new IllegalArgumentException("Null key returned for cache operation (maybe you are " +
"using named params on classes without debug info?) "
+ context.metadata.operation);
}
if (logger.isTraceEnabled()) {
logger.trace("Computed cache key '" + key + "' for operation " + context.metadata.operation);
}
return key;
}
private class CacheOperationContexts {
private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts;
private final boolean sync;
public CacheOperationContexts(Collection<? extends CacheOperation> operations, Method method,
Object[] args, Object target, Class<?> targetClass) {
this.contexts = new LinkedMultiValueMap<>(operations.size());
for (CacheOperation op : operations) {
/**
* 将 CacheOperation 转成 CacheOperationContext
* */
this.contexts.add(op.getClass(), getOperationContext(op, method, args, target, targetClass));
}
/**
* 推断是否同步。
*
* 是对 @Cacheable(sync=true) 的重重校验:
* 1. 没有 @Cacheable(sync=true) 就 `return false`
* 2. 有`sync=true`的情况
* 2.1 只允许写一个缓存注解
* 2.2 cacheNames 属性,只能写一个name
* 2.3 unless 属性,不能设置
* 注:不满足这三点就直接报错 `throw new IllegalStateException`
* */
this.sync = determineSyncFlag(method);
}
public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
Collection<CacheOperationContext> result = this.contexts.get(operationClass);
return (result != null ? result : Collections.emptyList());
}
public boolean isSynchronized() {
return this.sync;
}
private boolean determineSyncFlag(Method method) {
// 拿到 CacheableOperation 即 @Cacheable 的解析结果
List<CacheOperationContext> cacheOperationContexts = this.contexts.get(CacheableOperation.class);
if (cacheOperationContexts == null) { // no @Cacheable operation at all
return false;
}
boolean syncEnabled = false;
for (CacheOperationContext cacheOperationContext : cacheOperationContexts) {
// 是同步
if (((CacheableOperation) cacheOperationContext.getOperation()).isSync()) {
syncEnabled = true;
break;
}
}
// 是同步的,需要进一步校验
if (syncEnabled) {
// 开启同步,就不支持多个注解了
if (this.contexts.size() > 1) {
throw new IllegalStateException(
"@Cacheable(sync=true) cannot be combined with other cache operations on '" + method + "'");
}
// 咋感觉这一步有点多余呀
if (cacheOperationContexts.size() > 1) {
throw new IllegalStateException(
"Only one @Cacheable(sync=true) entry is allowed on '" + method + "'");
}
CacheOperationContext cacheOperationContext = cacheOperationContexts.iterator().next();
// 到这里,说明就写了一个 @Cacheable ,所以拿第一个就行
CacheableOperation operation = (CacheableOperation) cacheOperationContext.getOperation();
// 不支持多个 Cache
if (cacheOperationContext.getCaches().size() > 1) {
throw new IllegalStateException(
"@Cacheable(sync=true) only allows a single cache on '" + operation + "'");
}
// 不支持 unless 规则
if (StringUtils.hasText(operation.getUnless())) {
throw new IllegalStateException(
"@Cacheable(sync=true) does not support unless attribute on '" + operation + "'");
}
return true;
}
return false;
}
}
/**
* Metadata of a cache operation that does not depend on a particular invocation
* which makes it a good candidate for caching.
*/
protected static class CacheOperationMetadata {
private final CacheOperation operation;
private final Method method;
private final Class<?> targetClass;
private final Method targetMethod;
private final AnnotatedElementKey methodKey;
private final KeyGenerator keyGenerator;
private final CacheResolver cacheResolver;
public CacheOperationMetadata(CacheOperation operation, Method method, Class<?> targetClass,
KeyGenerator keyGenerator, CacheResolver cacheResolver) {
this.operation = operation;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetClass = targetClass;
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
/**
* Method对象+类型 作为 methodKey,在生成缓存key的时候会使用到,
* 也就是说即使是多例bean,也能保证执行同一个cache方法 也能生成同样的缓存key,从而可以使用同样的缓存值
* */
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
this.keyGenerator = keyGenerator;
this.cacheResolver = cacheResolver;
}
}
/**
* A {@link CacheOperationInvocationContext} context for a {@link CacheOperation}.
*/
protected class CacheOperationContext implements CacheOperationInvocationContext<CacheOperation> {
private final CacheOperationMetadata metadata;
/**
* 执行方法的入参。
* 注:如果最后一个参数是可变参数,会将可变参数铺平
*/
private final Object[] args;
private final Object target;
/**
* 这个 CacheOperation 对应的 Cache
*/
private final Collection<? extends Cache> caches;
/**
* 这个 CacheOperation 对应的 cacheName
*/
private final Collection<String> cacheNames;
@Nullable
private Boolean conditionPassing;
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
this.metadata = metadata;
/**
* 处理方法的最后一个参数是可变参数的情况。若是可变参数,就拿到最后一个参数,将其铺平,和其它参数放在一块
*
* 目的是:在生成缓存key时会根据方法参数来生成,方法的入参相同应当是同一个key。
* 但是动态参数的入参每次都是new数组来存储,这就导致方法的参数是一样的,但是动态参数是不同的对象,
* 所以这里需要将动态参数给铺平
* */
this.args = extractArgs(metadata.method, args);
this.target = target;
/**
* 使用 CacheResolver 通过注解设置的cacheName 拿到对应的 Cache 实例,
* 本质还是使用的 {@link CacheManager#getCache(String)},
* 常用的CacheManager都支持,没有预设Cache就在getCache的时候创建
* */
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
// 记录
this.cacheNames = createCacheNames(this.caches);
}
@Override
public CacheOperation getOperation() {
return this.metadata.operation;
}
@Override
public Object getTarget() {
return this.target;
}
@Override
public Method getMethod() {
return this.metadata.method;
}
@Override
public Object[] getArgs() {
return this.args;
}
/**
* 如果最后一个参数是可变参数,就将可变参数 铺平,和其它参数放在一块
* @param method 方法对象
* @param args 方法参数,指的是方法被调用时的参数
* @return
*/
private Object[] extractArgs(Method method, Object[] args) {
/**
* 比如 public void a(int i,String... x) 就是变量参数
* */
// 参数列表不是可变参数
if (!method.isVarArgs()) {
return args;
}
/**
* 因为最后一个参数是可变参数,所以拿到最后一个参数 铺平,和其它参数放在一块
* */
Object[] varArgs = ObjectUtils.toObjectArray(args[args.length - 1]);
Object[] combinedArgs = new Object[args.length - 1 + varArgs.length];
System.arraycopy(args, 0, combinedArgs, 0, args.length - 1);
System.arraycopy(varArgs, 0, combinedArgs, args.length - 1, varArgs.length);
return combinedArgs;
}
/**
* 是通过条件。
* - 有设置 @Cacheable(condition = "#root.methodName.startsWith('x')") 就解析SpEL看看结果是ture就是通过
* - 没有设置condition属性,就直接是true
* @param result
* @return
*/
protected boolean isConditionPassing(@Nullable Object result) {
/**
* 每次执行方法都会为每一个 CacheOperation 构造一个新的 CacheOperationContext 实例,所以这里可以这么判断
* {@link CacheAspectSupport#execute(CacheOperationInvoker, Object, Method, Object[])}
* */
if (this.conditionPassing == null) {
// 写了 condition 属性
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
// SpEL解析的构造上下文对象
EvaluationContext evaluationContext = createEvaluationContext(result);
// SpEL 解析表达式
this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
this.metadata.methodKey, evaluationContext);
} else {
// 没有写 condition 属性 不需要判断
this.conditionPassing = true;
}
}
return this.conditionPassing;
}
/**
* 可以设置缓存
* @param value
* @return
*/
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();
}
/**
* 写了 unless 条件
* @Cacheable(unless = "#result != null ")
* */
if (StringUtils.hasText(unless)) {
// 构造 EvaluationContext,就是设置根对象,方法参数的变量,返回值变量
EvaluationContext evaluationContext = createEvaluationContext(value);
// 解析 SpEL ,解析结果是 true 就是不可以更新缓存,所以是 !
return !evaluator.unless(unless, this.metadata.methodKey, evaluationContext);
}
// 没写 unless 直接返回true
return true;
}
/**
* 计算缓存的key。
* - 如果有设置 @Cacheable(key = "#p1"), SpEL的值+Method+Target生成的key 作为缓存的key
* - 没有指定key属性,就根据{@link KeyGenerator#generate(Object, Method, Object...)} 返回值作为缓存的key
*
* Compute the key for the given caching operation.
*/
@Nullable
protected Object generateKey(@Nullable Object result) {
// 有key
if (StringUtils.hasText(this.metadata.operation.getKey())) {
// SpEL解析的上下文对象
EvaluationContext evaluationContext = createEvaluationContext(result);
// 就是拿到 SpEL表达式的值+methodKey 作为key
return evaluator.key(this.metadata.operation.getKey(), this.metadata.methodKey, evaluationContext);
}
/**
* 没有key,就使用 keyGenerator 生成key
*
* 默认的SimpleKeyGenerator 是只根据args来生成key的 {@link SimpleKeyGenerator#generate(Object, Method, Object...)}
* */
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);
}
protected Collection<? extends Cache> getCaches() {
return this.caches;
}
protected Collection<String> getCacheNames() {
return this.cacheNames;
}
private Collection<String> createCacheNames(Collection<? extends Cache> caches) {
Collection<String> names = new ArrayList<>();
for (Cache cache : caches) {
names.add(cache.getName());
}
return names;
}
}
private class CachePutRequest {
private final CacheOperationContext context;
private final Object key;
public CachePutRequest(CacheOperationContext context, Object key) {
this.context = context;
this.key = key;
}
public void apply(@Nullable Object result) {
/**
* @Cacheable(unless = "#result != null ")
* 1. 指定了unless属性,就执行SpEL,结果是false 才可以设置缓存
* 2. 没有指定unless属性,就是可以设置缓存
* */
if (this.context.canPutToCache(result)) {
for (Cache cache : this.context.getCaches()) {
// 根据key往Cache中设置缓存
doPut(cache, this.key, result);
}
}
}
}
private static final class CacheOperationCacheKey implements Comparable<CacheOperationCacheKey> {
private final CacheOperation cacheOperation;
private final AnnotatedElementKey methodCacheKey;
private CacheOperationCacheKey(CacheOperation cacheOperation, Method method, Class<?> targetClass) {
this.cacheOperation = cacheOperation;
this.methodCacheKey = new AnnotatedElementKey(method, targetClass);
}
}
/**
* Internal holder class for recording that a cache method was invoked.
*/
private static class InvocationAwareResult {
boolean invoked;
}
}
public abstract class AbstractCacheInvoker {
/**
* 默认是 SimpleCacheErrorHandler
*/
protected SingletonSupplier<CacheErrorHandler> errorHandler;
/**
* Return the {@link CacheErrorHandler} to use.
*/
public CacheErrorHandler getErrorHandler() {
return this.errorHandler.obtain();
}
/**
* Execute {@link Cache#get(Object)} on the specified {@link Cache} and
* invoke the error handler if an exception occurs. Return {@code null}
* if the handler does not throw any exception, which simulates a cache
* miss in case of error.
* @see Cache#get(Object)
*/
@Nullable
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
try {
return cache.get(key);
} catch (RuntimeException ex) {
getErrorHandler().handleCacheGetError(ex, cache, key);
return null; // If the exception is handled, return a cache miss
}
}
/**
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
* and invoke the error handler if an exception occurs.
*/
protected void doPut(Cache cache, Object key, @Nullable Object result) {
try {
cache.put(key, result);
} catch (RuntimeException ex) {
getErrorHandler().handleCachePutError(ex, cache, key, result);
}
}
/**
* Execute {@link Cache#evict(Object)}/{@link Cache#evictIfPresent(Object)} on the
* specified {@link Cache} and invoke the error handler if an exception occurs.
*/
protected void doEvict(Cache cache, Object key, boolean immediate) {
try {
if (immediate) {
// 移除key对应的缓存
cache.evictIfPresent(key);
} else {
// 移除key对应的缓存
cache.evict(key);
}
} catch (RuntimeException ex) {
getErrorHandler().handleCacheEvictError(ex, cache, key);
}
}
/**
* Execute {@link Cache#clear()} on the specified {@link Cache} and
* invoke the error handler if an exception occurs.
*/
protected void doClear(Cache cache, boolean immediate) {
try {
if (immediate) {
// 也是清空,就看具体的 Cache 有啥特殊写法了
cache.invalidate();
} else {
// 清空整个缓存
cache.clear();
}
} catch (RuntimeException ex) {
// 处理异常
getErrorHandler().handleCacheClearError(ex, cache);
}
}
}
执行流程
@EnableCaching注解会import到容器CachingConfigurationSelector类,而CachingConfigurationSelector会向容器中注入AutoProxyRegistrar和ProxyCachingConfiguration。AutoProxyRegistrar在事务那里讲过了,这里主要看ProxyCachingConfiguration。
// 和事务一样的套路,向容器中注入切面、切点和通知,主要看通知的增强逻辑。
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor(
CacheOperationSource cacheOperationSource, CacheInterceptor cacheInterceptor) {
// 就是增强器咯
BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
// 用来解析注解的,通过解析注解从而知道是否需要代理
advisor.setCacheOperationSource(cacheOperationSource);
// 增强逻辑
advisor.setAdvice(cacheInterceptor);
if (this.enableCaching != null) {
advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
}
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
/**
* 用来解析 方法->类 上 @Caching、@Cacheable、@CacheEvict、@CachePut、@CacheConfig 的
* */
return new AnnotationCacheOperationSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor(CacheOperationSource cacheOperationSource) {
CacheInterceptor interceptor = new CacheInterceptor();
// 配置
interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
// 用来解析 @Caching、@Cacheable、@CacheEvict、@CachePut 的
interceptor.setCacheOperationSource(cacheOperationSource);
return interceptor;
}
}
由上述代码可以看到,增强逻辑在CacheInterceptor中。而CacheInterceptor其实什么也没做,调用的是父类CacheAspectSupport的execute方法。
代码如下,可以看到CacheAspectSupport将解析出来的所有注解信息封装到了CacheOperationContexts中。
CacheAspectSupport:
@Nullable
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
/**
* 这个bean实例化完就会设置为true
* {@link CacheAspectSupport#afterSingletonsInstantiated()}
* */
// Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
if (this.initialized) {
// 被代理类
Class<?> targetClass = getTargetClass(target);
// 拿到解析缓存注解的工具
CacheOperationSource cacheOperationSource = getCacheOperationSource();
if (cacheOperationSource != null) {
// 返回的是方法或者类上 @Cacheable、@CacheEvict、@CachePut 的解析结果
Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
// 不是空,表示方法上有缓存注解
if (!CollectionUtils.isEmpty(operations)) {
/**
* 将 operations、方法、方法参数、执行方法的对象和执行方法的类型 装饰成 CacheOperationContexts 对象
* 然后 invoke
* */
return execute(invoker, method,
new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
}
return invoker.invoke();
}
CacheOperationContexts中存储了所有的注解信息,每个注解信息又被封装成了CacheOperationContext信息。
CacheOperationContext又是啥玩意,它里面其实就是对注解信息的进一步封装,其中的CacheOperationMetadata解析出了注解指定的KeyGenerator以及CacheResolver,后面要用。
综上所属,注解的信息都有了,下面的execute便是使用这些信息进行缓存的操作。
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
/**
* 是同步的。就是 @Cacheable(sync = true)
* */
// Special handling of synchronized invocation
if (contexts.isSynchronized()) {
// 同步的方法上只允许有一个 缓存注解,所以拿第一个就行了
CacheOperationContext context = contexts.get(CacheableOperation.class).iterator().next();
/**
* 条件通过。
* 有设置 @Cacheable(condition = "#root.methodName.startsWith('x')") 就解析SpEL看看结果是true就是通过
* 没有设置condition属性,就直接是true
*
* 很简单,就是将 方法的参数和一些固定参数(method、target、targetClass、args、caches) 构造出 EvaluationContext 然后使用 SpelExpressionParser 解析 SpEL表达式
* {@link CacheOperationContext#isConditionPassing(Object)}
* */
if (isConditionPassing(context, CacheOperationExpressionEvaluator.NO_RESULT)) {
/**
* 生成key。
* 如果有设置 @Cacheable(key = "#p1"), SpEL的值+Method+Target生成的key 作为缓存的key
* 没有指定key属性,就根据{@link KeyGenerator#generate(Object, Method, Object...)} 返回值作为缓存的key
* */
Object key = generateKey(context, CacheOperationExpressionEvaluator.NO_RESULT);
// 同步的方法上只允许有一个 缓存注解,所以拿第一个Cache实例就行了
Cache cache = context.getCaches().iterator().next();
try {
/**
* handleSynchronizedGet 就是 使用 key从Cache中获取,获取不到就invoke方法获取值,会将方法返回值值存到 Cache 中。
*
* 存入Cache的细节:
* 1. 如果方法返回值是Optional类型的,会 {@link Optional#get()} 存到缓存
* 2. 如果Cache配了序列化器,会将值序列化在存到缓存中,获取的时候也会判断是否有序列化器 有就反序列化值
*
* wrapCacheValue 就是如果方法的返回值是 Optional 类型的,就包装一下
*
* 注:并没有看到具体的同步操作,是不是假的同步呀 能不能同步 还得看具体的 {@link Cache#get(Object)} 如何实现的
* 莫非,所谓的同步就是校验 方法上只能有一个 @Cacheable 注解???
* 看了官方文档,这个同步的实现得看具体的Cache实例 https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache-annotations-cacheable-synchronized
* */
return wrapCacheValue(method, handleSynchronizedGet(invoker, key, cache));
} catch (Cache.ValueRetrievalException ex) {
// Directly propagate ThrowableWrapper from the invoker,
// or potentially also an IllegalArgumentException etc.
ReflectionUtils.rethrowRuntimeException(ex.getCause());
}
} else {
// 不符合缓存条件,反射调用方法返回
// No caching required, only call the underlying method
return invokeOperation(invoker);
}
}
/**
* 清空缓存,就看是根据key删除 还是 直接清空整个Cache
* 比如 @CacheEvict(allEntries = true) 就是清空整个缓存
* */
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
CacheOperationExpressionEvaluator.NO_RESULT);
/**
* 就是遍历 CacheOperationContext,条件通过的,在遍历 Cache 根据key获取缓存值,找到就返回
* */
// Check if we have a cached item matching the conditions
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));
/**
* 就是没有缓存的值,那就构造 CachePutRequest ,就是用来将 方法返回值 存到 Cache中的
* {@link CachePutRequest#apply(Object)}
*
* 该属性是记录符合条件的 @CachePut + @Cacheable
* */
// Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new ArrayList<>();
if (cacheHit == null) {
// 收集满足 condition 的 CacheableOperation
collectPutRequests(contexts.get(CacheableOperation.class),
CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Object cacheValue;
Object returnValue;
/**
* 有缓存 且 没有符合条件的的@CachePut
*
* 那就不需要更新,那就直接拿缓存的值就行了
* */
if (cacheHit != null && !hasCachePut(contexts)) {
// 拿到缓存的值
// If there are no put requests, just use the cache hit
cacheValue = cacheHit.get();
// 是否需要装饰为Optional对象
returnValue = wrapCacheValue(method, cacheValue);
} else {
// 放行方法,拿到返回值
// Invoke the method if we don't have a cache hit
returnValue = invokeOperation(invoker);
// 是否需要解构Optional对象,拿到其值
cacheValue = unwrapReturnValue(returnValue);
}
/**
* 收集符合条件的@CachePut
* */
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
/**
* 就是就方法返回值 设置到 Cache 中,此时会进行 unless 的解析判断
* {@link CacheOperationContext#canPutToCache(Object)}
* */
cachePutRequest.apply(cacheValue);
}
// 清空缓存
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
// 返回
return returnValue;
}
我们先看一下清空缓存的逻辑:
private void processCacheEvicts(
Collection<CacheOperationContext> contexts, boolean beforeInvocation, @Nullable Object result) {
// 遍历所有的 CacheEvictOperation
for (CacheOperationContext context : contexts) {
CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
/**
* CacheEvictOperation 是调用前执行 且 满足条件
* 就是 @CacheEvict(beforeInvocation = true)
* */
if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
// 清空缓存
performCacheEvict(context, operation, result);
}
}
}
private void performCacheEvict(
CacheOperationContext context, CacheEvictOperation operation, @Nullable Object result) {
Object key = null;
// 遍历关联的Cache
for (Cache cache : context.getCaches()) {
/**
* 就是 @CacheEvict(allEntries = true)
* */
if (operation.isCacheWide()) {
logInvalidating(context, operation, null);
// 清空整个 Cache
doClear(cache, operation.isBeforeInvocation());
} else {
if (key == null) {
// 缓存的key
key = generateKey(context, result);
}
logInvalidating(context, operation, key);
// 根据 key 清除 Cache 中存储的值
doEvict(cache, key, operation.isBeforeInvocation());
}
}
}
再看一下查找缓存的信息:
@Nullable
private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
Object result = CacheOperationExpressionEvaluator.NO_RESULT;
// 就是方法上写了多个 @Cacheable
for (CacheOperationContext context : contexts) {
// 匹配 这里Spel表达式去解析
if (isConditionPassing(context, result)) {
// 生成key
Object key = generateKey(context, result);
/**
* 因为一个 @Cacheable(cacheNames = {"a", "b"}) 是可以写多个 Cache的,所以会遍历所有的 Cache 根据key找,先找到就返回
* */
Cache.ValueWrapper cached = findInCaches(context, key);
// 也就是说 先命中就返回其值
if (cached != null) {
return cached;
} else {
if (logger.isTraceEnabled()) {
logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
}
}
}
}
return null;
}
@Nullable
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
// 遍历 Cache
for (Cache cache : context.getCaches()) {
// 就是根据 key 获取咯
Cache.ValueWrapper wrapper = doGet(cache, key);
if (wrapper != null) {
if (logger.isTraceEnabled()) {
logger.trace("Cache entry for key '" + key + "' found in cache '" + cache.getName() + "'");
}
return wrapper;
}
}
return null;
}
再来看一下put的代码:
public void apply(@Nullable Object result) {
/**
* @Cacheable(unless = "#result != null ")
* 1. 指定了unless属性,就执行SpEL,结果是false 才可以设置缓存
* 2. 没有指定unless属性,就是可以设置缓存
* */
if (this.context.canPutToCache(result)) {
for (Cache cache : this.context.getCaches()) {
// 根据key往Cache中设置缓存
doPut(cache, this.key, result);
}
}
}
@CacheEvict(beforeInvocation = true)
:- 条件判断无法获取方法的返回值,是在方法执行前判断。
@Cacheable
:condition
属性是在方法执行前判断,无法获取方法的返回值。unless
属性是在方法执行后判断或者命中了缓存,可以获取方法的返回值或者是缓存的值。
@CachePut
:condition
属性是在方法执行前判断,无法获取方法的返回值。unless
属性是在方法执行后判断或者命中了缓存,可以获取方法的返回值或者是缓存的值。
@CacheEvict(beforeInvocation = false)
:- 条件判断可以获取方法的返回值,是在方法执行后判断。
CacheEvaluationContext 的变量解析过程,这就是为啥注解中写的SpEL表达式能用 #p1 #a1 #name 引用方法入参的原理
public class MethodBasedEvaluationContext extends StandardEvaluationContext {
private final Method method;
private final Object[] arguments;
private final ParameterNameDiscoverer parameterNameDiscoverer;
private boolean argumentsLoaded = false;
public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments,
ParameterNameDiscoverer parameterNameDiscoverer) {
super(rootObject);
this.method = method;
this.arguments = arguments;
// 解析参数列表 参数名称的
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* SpEL 在使用变量时,会回调该方法返回变量
* {@link StandardEvaluationContext#lookupVariable(String)}
* @param name variable to lookup
* @return
*/
@Override
@Nullable
public Object lookupVariable(String name) {
Object variable = super.lookupVariable(name);
// 不为 null 就返回
if (variable != null) {
return variable;
}
// 还未加载过
if (!this.argumentsLoaded) {
// 加载参数
lazyLoadArguments();
this.argumentsLoaded = true;
// 再次执行 就能查到了,查不到那就是真的没有
variable = super.lookupVariable(name);
}
// 返回
return variable;
}
/**
* Load the param information only when needed.
*/
protected void lazyLoadArguments() {
// Shortcut if no args need to be loaded
if (ObjectUtils.isEmpty(this.arguments)) {
return;
}
// 方法的参数列表名称
// Expose indexed variables as well as parameter names (if discoverable)
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
// 这是方法的参数列表名称
int paramCount = (paramNames != null ? paramNames.length : this.method.getParameterCount());
/**
* 这个是方法执行时,传入的参数个数
*
* 这个是在实例化 CacheOperationContext {@link CacheAspectSupport.CacheOperationContext#CacheOperationContext(CacheAspectSupport.CacheOperationMetadata, Object[], Object)}
* 解析方法的入参。解析入参,如果方法的最后一个参数是可变参数,会将可变参数铺平和其余入参放在数组中。
*
* 比如:
* public abstract void a(String s,String... ss);
* */
int argsCount = this.arguments.length;
// 遍历
for (int i = 0; i < paramCount; i++) {
Object value = null;
/**
* argsCount > paramCount 就是 方法的最后一个参数是可变参数 的情况
* */
if (argsCount > paramCount && i == paramCount - 1) {
// 收集成数组
// Expose remaining arguments as vararg array for last parameter
value = Arrays.copyOfRange(this.arguments, i, argsCount);
} else if (argsCount > i) {
// Actual argument found - otherwise left as null
value = this.arguments[i];
}
/**
* 设置SpEL表达式变量
*
* "#a1 != null"
* "#p1 != null"
* "#ss != null"
* */
setVariable("a" + i, value);
setVariable("p" + i, value);
// 设置方法参数名为变量名
if (paramNames != null && paramNames[i] != null) {
setVariable(paramNames[i], value);
}
}
}
}
Cache支持的SpEL变量说明:
Name | Location | Description | Example |
---|---|---|---|
methodName | Root object | 正在调用的方法的名称 | #root.methodName |
method | Root object | 正在调用的方法的名称 | #root.method.name |
target | Root object | 正在调用的方法的对象 | #root.target |
targetClass | Root object | 正在调用的方法的对象的类 | #root.targetClass |
args | Root object | 当前调用的参数列表 | #root.args[0] |
caches | Root object | 当前方法使用的Cache列表 | #root.caches[0].name |
Argument name | Evaluation context | 当前调用方法参数的变量 | #name | #a1 | #p1 |
result | Evaluation context | 方法执行的返回值 | #result |
注:还可以使用 @beanName
引用BeanFactory中的bean对象