Spring 缓存注解解析过程

通过 SpringCacheAnnotationParser 的 parseCacheAnnotations 方法解析指定方法或类上的缓存注解,
@Cacheable 注解将被解析为 CacheableOperation 操作,
@CachePut 注解将被解析为 CachePutOperation 操作,
@CacheEvict 注解将被解析为 CacheEvictOperation 操作。

缓存注解

/**
* 启用Spring以注解驱动的缓存管理功能
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
/**
* 是否启用 CGLIB 类代理,默认是 JDK 动态代理
*/
boolean proxyTargetClass() default false; /**
* 指示如何应用缓存通知,默认是 JDK Proxy
*/
AdviceMode mode() default AdviceMode.PROXY; /**
* 当多个 Adviser 将通知织入连接点时,缓存通知的优先级
*/
int order() default Ordered.LOWEST_PRECEDENCE;
} /**
* 在类级别设置共享的缓存配置信息
* @since 4.1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheConfig {
/**
* 默认缓存名称
*/
String[] cacheNames() default {}; /**
* 默认的键生成器 bean 名称
* org.springframework.cache.interceptor.KeyGenerator
*/
String keyGenerator() default ""; /**
* 默认的缓存管理器 bean 名称
* org.springframework.cache.CacheManager
*/
String cacheManager() default ""; /**
* 默认的缓存解析器 bean 名称
* org.springframework.cache.interceptor.CacheResolver
*/
String cacheResolver() default "";
} /**
* 表明类中单个方法【作用于方法】或所有方法的返回值【作用于类】能被缓存
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable { /**
* cacheNames 别名
*/
@AliasFor("cacheNames")
String[] value() default {}; /**
* 关联缓存名称数组
* @since 4.2
*/
@AliasFor("value")
String[] cacheNames() default {}; /**
* 动态计算缓存键的 SpEL 表达式
*/
String key() default ""; /**
* 缓存键生成器 bean 名称
*/
String keyGenerator() default ""; /**
* 缓存管理器 bean 名称
*/
String cacheManager() default ""; /**
* 缓存解析器 bean 名称
*/
String cacheResolver() default ""; /**
* 缓存操作的生效条件【SpEL 表达式】,不指定默认生效
*/
String condition() default ""; /**
* 否决方法缓存的条件【SpEL 表达式】,默认不匹配【返回 true 表示不缓存】
*/
String unless() default ""; /**
* 多线程调用方法时,是否执行同步调用
* <ol>
* <li>{@link #unless()} is not supported</li>
* <li>Only one cache may be specified</li>
* <li>No other cache-related operation can be combined</li>
* </ol>
* @since 4.3
*/
boolean sync() default false;
} /**
* 指定的方法调用或类中所有方法调用,需要执行 CachePut 操作将结果值进行缓存
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CachePut {
/**
* cacheNames 别名
*/
@AliasFor("cacheNames")
String[] value() default {}; /**
* 关联缓存名称数组
* @since 4.2
*/
@AliasFor("value")
String[] cacheNames() default {}; /**
* 动态计算缓存键的 SpEL 表达式
*/
String key() default ""; /**
* 缓存键生成器 bean 名称
*/
String keyGenerator() default ""; /**
* 缓存管理器 bean 名称
*/
String cacheManager() default ""; /**
* 缓存解析器 bean 名称
*/
String cacheResolver() default ""; /**
* 缓存操作的生效条件【SpEL 表达式】,不指定默认生效
*/
String condition() default ""; /**
* 否决方法缓存的条件【SpEL 表达式】,默认不匹配【返回 true 表示不缓存】
* @since 3.2
*/
String unless() default "";
} /**
* 指定的方法调用或所有方法调用,需要执行一个缓存清除操作。
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheEvict {
/**
* cacheNames 别名
*/
@AliasFor("cacheNames")
String[] value() default {}; /**
* 关联缓存名称数组
* @since 4.2
*/
@AliasFor("value")
String[] cacheNames() default {}; /**
* 动态计算缓存键的 SpEL 表达式
*/
String key() default ""; /**
* 缓存键生成器 bean 名称
*/
String keyGenerator() default ""; /**
* 缓存管理器 bean 名称
*/
String cacheManager() default ""; /**
* 缓存解析器 bean 名称
*/
String cacheResolver() default ""; /**
* 缓存操作的生效条件【SpEL 表达式】,不指定默认生效
*/
String condition() default ""; /**
* 是否删除缓存中的所有条目,默认只删除缓存键关联的条目
*/
boolean allEntries() default false; /**
* 缓存清除操作是否需要在方法调用前执行
*/
boolean beforeInvocation() default false;
}

缓存注解解析器

/**
* 用于解析缓存注解的策略接口
*/
public interface CacheAnnotationParser { /**
* 基于注释类型解析指定类的缓存定义
*/
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Class<?> type); /**
* 基于注释类型解析指定方法的缓存定义
*/
@Nullable
Collection<CacheOperation> parseCacheAnnotations(Method method);
} /**
* 用于解析 @Caching、@Cacheable、@CacheEvict、@CachePut 注解的解析器
*/
@SuppressWarnings("serial")
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
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
// 创建默认的缓存配置
final DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
} /**
* 解析指定方法上的缓存注解
*/
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
// 创建默认的缓存配置
final DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
} /**
* 基于默认的缓存配置解析缓存注解
*/
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
final Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
if (ops != null && ops.size() > 1) {
// 如果发现多于 1 个缓存操作,则类中的缓存操作覆盖接口中的缓存操作
final Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
if (localOps != null) {
return localOps;
}
}
return ops;
} /**
* 解析指定注解元素上的缓存注解
*
* @param cachingConfig 缓存配置
* @param ae 注解元素
* @param localOnly 是否只解析当前类中的缓存注解【缓存注解也可以在接口上使用】
*/
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae,
boolean localOnly) {
// 读取所有的缓存注解
final 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);
// 解析 Cacheable 注解
anns.stream().filter(ann -> ann instanceof Cacheable)
.forEach(ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann)));
// 解析 CacheEvict 注解
anns.stream().filter(ann -> ann instanceof CacheEvict)
.forEach(ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann)));
// 解析 CachePut 注解
anns.stream().filter(ann -> ann instanceof CachePut)
.forEach(ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann)));
// 解析 Caching 注解
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) {
final 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);
final CacheableOperation op = builder.build();
/**
* 验证缓存操作
* key 和 keyGenerator 不可同时配置
* cacheManager 和 cacheResolver 不可同时配置
*/
validateCacheOperation(ae, op);
return op;
} private CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig,
CacheEvict cacheEvict) {
final 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);
final CacheEvictOperation op = builder.build();
validateCacheOperation(ae, op); return op;
} private CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig,
CachePut cachePut) {
final 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);
final CachePutOperation op = builder.build();
validateCacheOperation(ae, op); return op;
} private void parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching,
Collection<CacheOperation> ops) {
final Cacheable[] cacheables = caching.cacheable();
for (final Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
}
final CacheEvict[] cacheEvicts = caching.evict();
for (final CacheEvict cacheEvict : cacheEvicts) {
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
}
final CachePut[] cachePuts = caching.put();
for (final CachePut cachePut : cachePuts) {
ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
}
} /**
* Validates the specified {@link CacheOperation}.
* <p>Throws an {@link IllegalStateException} if the state of the operation is
* invalid. As there might be multiple sources for default values, this ensure
* that the operation is in a proper state before being returned.
* @param ae the annotated element of the cache operation
* @param operation the {@link CacheOperation} to validate
*/
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.");
}
} @Override
public boolean equals(Object other) {
return this == other || other instanceof SpringCacheAnnotationParser;
} @Override
public int hashCode() {
return SpringCacheAnnotationParser.class.hashCode();
} /**
* 为给定的缓存操作集提供默认配置
*/
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;
} /**
* Apply the defaults to the specified {@link CacheOperation.Builder}.
*/
public void applyDefault(CacheOperation.Builder builder) {
if (!initialized) {
// 查找目标类型上的 CacheConfig 注解配置,如果存在则写入默认配置
final CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(target, CacheConfig.class);
if (annotation != null) {
cacheNames = annotation.cacheNames();
keyGenerator = annotation.keyGenerator();
cacheManager = annotation.cacheManager();
cacheResolver = annotation.cacheResolver();
}
initialized = true;
} // 指定注解未设置缓存名称 && 默认配置不为 null && 写入默认配置
if (builder.getCacheNames().isEmpty() && cacheNames != null) {
builder.setCacheNames(cacheNames);
}
// 指定注解未指定键和键生成器 && 默认配置不为 null && 写入键生成器
if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator())
&& StringUtils.hasText(keyGenerator)) {
builder.setKeyGenerator(keyGenerator);
}
// 未指定缓存管理器和缓存解析器,则默认的 cacheResolver 优先级高于 cacheManager
if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
// One of these is set so we should not inherit anything
} else if (StringUtils.hasText(cacheResolver)) {
builder.setCacheResolver(cacheResolver);
} else if (StringUtils.hasText(cacheManager)) {
builder.setCacheManager(cacheManager);
}
}
}
}

缓存操作

/**
* 所有缓存操作必须实现的基本接口
*/
public interface BasicOperation {
/**
* 返回与此操作关联的所有缓存名称
*/
Set<String> getCacheNames();
} /**
* 缓存操作基础类
*/
public abstract class CacheOperation implements BasicOperation {
/**
* 操作名称
*/
private final String name;
/**
* 关联缓存名称
*/
private final Set<String> cacheNames;
/**
* 缓存键
*/
private final String key;
/**
* 缓存键生成器名称
*/
private final String keyGenerator;
/**
* 缓存管理器名称
*/
private final String cacheManager;
/**
* 缓存解析器名称
*/
private final String cacheResolver;
/**
* 缓存条件
*/
private final String condition;
/**
* 操作的字符串表示
*/
private final String toString;
} /**
* 对应于 @Cacheable 注解的操作
*/
public class CacheableOperation extends CacheOperation {
/**
* 缓存条件
*/
@Nullable
private final String unless;
/**
* 此操作是否是同步的
*/
private final boolean sync;
} /**
* 对应于 @CacheEvict 注解的操作
*/
public class CacheEvictOperation extends CacheOperation {
/**
* 是否删除缓存中的所有条目
*/
private final boolean cacheWide;
/**
* 是否在目标方法调用之前执行
*/
private final boolean beforeInvocation;
} /**
* 对应于 @CachePut 注解的操作
*/
public class CachePutOperation extends CacheOperation {
/**
* 缓存条件
*/
@Nullable
private final String unless;
}

缓存操作源

/**
* 被 CacheInterceptor 使用的缓存操作源
*/
public interface CacheOperationSource { /**
* 获取目标类中指定方法的所有缓存操作
*/
@Nullable
Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass); } /**
* 带兜底策略的缓存操作源,缓存注解解析顺序如下
* 1. specific target method;
* 2. target class;
* 3. declaring method;
* 4. declaring class/interface.
*/
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
/**
* Canonical value held in cache to indicate no caching attribute was
* found for this method and we don't need to look again.
*/
private static final Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList(); protected final Log logger = LogFactory.getLog(getClass()); /**
* 缓存操作的缓存
*/
private final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<>(1024); /**
* 确定此方法调用的缓存属性
*/
@Override
@Nullable
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// 创建缓存键
final Object cacheKey = getCacheKey(method, targetClass);
// 读取操作缓存
final Collection<CacheOperation> cached = attributeCache.get(cacheKey); // 1)如果存在则直接返回
if (cached != null) {
return cached != NULL_CACHING_ATTRIBUTE ? cached : null;
}
else {
// 2)解析指定方法上的缓存操作并缓存
final Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
if (cacheOps != null) {
if (logger.isTraceEnabled()) {
logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
}
attributeCache.put(cacheKey, cacheOps);
}
else {
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 方法的缓存注解【默认 false】
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
} /**
* The method may be on an interface, but we need attributes from the target class.
* If the target class is null, the method will be unchanged.
*/
final Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // 1)First try is the method in the target class.
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
if (opDef != null) {
return opDef;
} // 2)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;
} @Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz); @Nullable
protected abstract Collection<CacheOperation> findCacheOperations(Method method); protected boolean allowPublicMethodsOnly() {
return false;
}
} @SuppressWarnings("serial")
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource implements Serializable {
/**
* 是否只解析 public 方法,默认为 true
*/
private final boolean publicMethodsOnly;
/**
* 缓存注解解析器
*/
private final Set<CacheAnnotationParser> annotationParsers; public AnnotationCacheOperationSource() {
this(true);
} public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
annotationParsers = Collections.singleton(new SpringCacheAnnotationParser());
} public AnnotationCacheOperationSource(CacheAnnotationParser annotationParser) {
publicMethodsOnly = true;
Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
annotationParsers = Collections.singleton(annotationParser);
} public AnnotationCacheOperationSource(CacheAnnotationParser... annotationParsers) {
publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
this.annotationParsers = new LinkedHashSet<>(Arrays.asList(annotationParsers));
} public AnnotationCacheOperationSource(Set<CacheAnnotationParser> annotationParsers) {
publicMethodsOnly = true;
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
this.annotationParsers = annotationParsers;
} @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));
} /**
* 解析缓存注解并转换为 CacheOperation
*/
@Nullable
protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
Collection<CacheOperation> ops = null;
for (final CacheAnnotationParser annotationParser : annotationParsers) {
final Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
if (annOps != null) {
if (ops == null) {
ops = annOps;
}
else {
final Collection<CacheOperation> combined = new ArrayList<>(ops.size() + annOps.size());
combined.addAll(ops);
combined.addAll(annOps);
ops = combined;
}
}
}
return ops;
} @Override
protected boolean allowPublicMethodsOnly() {
return publicMethodsOnly;
} @Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AnnotationCacheOperationSource)) {
return false;
}
final AnnotationCacheOperationSource otherCos = (AnnotationCacheOperationSource) other;
return annotationParsers.equals(otherCos.annotationParsers) &&
publicMethodsOnly == otherCos.publicMethodsOnly;
} @Override
public int hashCode() {
return annotationParsers.hashCode();
} @FunctionalInterface
protected interface CacheOperationProvider {
/**
* 返回指定缓存解析解析成功的缓存操作
*/
@Nullable
Collection<CacheOperation> getCacheOperations(CacheAnnotationParser parser);
} }

Spring 缓存注解解析过程的更多相关文章

  1. spring ioc 源码分析之-- beanDefinition的加载过程以及ComponentScan,@componet,@import @Bean等注解解析过程

    背景:我们启动主启动类后,相应的bean就被扫描进来了,原理是啥? 实现该功能的主要核心类就是:ConfigurationClassPostProcessor,我们看看他的继承体系: 它实现了Bean ...

  2. Spring 缓存注解 SpEL 表达式解析

    缓存注解上 key.condition.unless 等 SpEL 表达式的解析 SpEl 支持的计算变量: 1)#ai.#pi.#命名参数[i 表示参数下标,从 0 开始] 2)#result:Ca ...

  3. Spring – 缓存注解

    Spring缓存抽象概述 Spring框架自身并没有实现缓存解决方案,但是从3.1开始定义了org.springframework.cache.Cache和org.springframework.ca ...

  4. 1-spring xml 和 注解 解析过程

    spring mvc 入口 DispatcherServlet,类关系图如下所示 DispatcherServlet 就是一个 Servlet,那Servlet 的初始化方法 init()在哪里,通过 ...

  5. Spring缓存注解@CachePut , @CacheEvict,@CacheConfig使用

    Cacheable CachePut CacheEvict CacheConfig 开启缓存注解 @Cacheable @Cacheable是用来声明方法是可缓存的.将结果存储到缓存中以便后续使用相同 ...

  6. Spring缓存注解@Cache使用

    参考资料 http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ http://swiftlet.net/archive ...

  7. Spring缓存注解@Cacheable、@CacheEvict、@CachePut使用(转)

    原文地址:https://www.cnblogs.com/fashflying/p/6908028.html 从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对 ...

  8. Spring 缓存注解之@Cacheable,@CacheEvit

    Spring引入了Cache的支持,其使用方法类似于Spring对事务的支持.Spring Cache是作用于方法上的,其核心思想是,当我们调用一个缓存方法时,把该方法参数和返回结果作为一个键值对存放 ...

  9. 【Spring】22、Spring缓存注解@Cache使用

    缓存注解有以下三个: @Cacheable      @CacheEvict     @CachePut @Cacheable(value=”accountCache”),这个注释的意思是,当调用这个 ...

随机推荐

  1. JVM内存分配和垃圾回收以及性能调优

    JVM内存分配策略 一:堆中优先分配Eden 大多数情况下,对象都在新生代的Eden区中分配内存.而新生代会频繁进行垃圾回收. 二:大对象直接进入老年代 需要大量连续空间的对象,如:长字符串.数组等, ...

  2. VIM如何自动保存文件、自动重加载文件、自动刷新显示文件

    1.手动重加载文件的命令是:e! 2.一劳永逸的方法是:vim提供了自动加载的选项 autoread,默认关闭. 在vimrc中添加 set autoread即可打开自动加载选项,相关选项: :hel ...

  3. 2019牛客暑期多校训练营(第一场) B Integration (数学)

    链接:https://ac.nowcoder.com/acm/contest/881/B 来源:牛客网 Integration 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 5242 ...

  4. Sublime Text2 常用快捷键总结

    Ctrl+Tab 当前窗口中的标签页切换 Ctrl+Shift+D 复制光标所在整行,插入在该行之前 Ctrl+Shift+K 删除整行 Ctrl+Shift+/ 注释已选择内容 Ctrl+Shift ...

  5. 一、Linux 设备驱动介绍及开发环境搭建

    1.1 Linux 设备驱动介绍 1.1.1 分类及特点 计算机系统的硬件主要由 CPU.存储器和外设组成. 当前 CPU 内部都集成有存储器和外设适配器. 外设适配器有入 UART.IIC 控制器. ...

  6. ESP8266网络介绍

    仔细分析上图,根据功能区分,可以分为: Arduino功能,把ESP8266 当做 Arduino来使用 SD —— SD卡库 Servo —— 伺服电机库 Wire —— I2C库 SPI —— s ...

  7. java——SimpleDateFormat与DateTimeFormatter

    https://www.jianshu.com/p/b212afa16f1f SimpleDateFormat不是线程安全的 DateTimeFormatter是线程安全的

  8. [转]CSS自动换行后缩进

    原文 https://blog.csdn.net/u011974797/article/details/71439794 例如: ●这是第一行太长了超出 显示到第二行 想实现的效果: ●这是第一行太长 ...

  9. Let Us Adore 让我们来敬拜祂 中文歌词

      Verse 1 诸天宣告 神的荣耀 万国万民 都将赞美 宣扬祂奇妙 The heavens declare The glory of God And all of the world Will j ...

  10. 如何查看 mysql 的视图?

    1.查询表(包括view) mysql> use iips; Database changed mysql> show tables; +------------------------- ...