+ add support for multiple cache names
+ require each annotation to specify a cache name
+ add method support in Key generator interface
+ add bug fix for embedded JDK concurrent declaration

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3819 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Costin Leau 2010-12-16 13:19:01 +00:00
parent 2c7f153336
commit a118023af6
17 changed files with 159 additions and 94 deletions

View File

@ -1,4 +1,4 @@
#Thu Dec 18 06:34:28 PST 2008 #Wed Mar 31 18:40:01 EEST 2010
eclipse.preferences.version=1 eclipse.preferences.version=1
formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile formatter_profile=_Spring
formatter_settings_version=11 formatter_settings_version=11

View File

@ -16,14 +16,15 @@
package org.springframework.cache; package org.springframework.cache;
import java.lang.reflect.Method;
/** /**
* Cache 'key' extractor. Used for creating a key based on the given * Cache 'key' extractor. Used for creating a key based on the given method
* parameters. * (used as context) and its parameter.
* *
* @author Costin Leau * @author Costin Leau
*/ */
// CL: could be renamed to KeyFactory
public interface KeyGenerator<K> { public interface KeyGenerator<K> {
K extract(Object... params); K extract(Method method, Object... params);
} }

View File

@ -107,7 +107,7 @@ public class AnnotationCacheDefinitionSource extends AbstractFallbackCacheDefini
*/ */
protected CacheDefinition determineCacheDefinition(AnnotatedElement ae) { protected CacheDefinition determineCacheDefinition(AnnotatedElement ae) {
for (CacheAnnotationParser annotationParser : this.annotationParsers) { for (CacheAnnotationParser annotationParser : this.annotationParsers) {
CacheDefinition attr = annotationParser.parseTransactionAnnotation(ae); CacheDefinition attr = annotationParser.parseCacheAnnotation(ae);
if (attr != null) { if (attr != null) {
return attr; return attr;
} }

View File

@ -42,5 +42,5 @@ public interface CacheAnnotationParser {
* or <code>null</code> if none was found * or <code>null</code> if none was found
* @see AnnotationCacheDefinitionSource#determineCacheOperationDefinition * @see AnnotationCacheDefinitionSource#determineCacheOperationDefinition
*/ */
CacheDefinition parseTransactionAnnotation(AnnotatedElement ae); CacheDefinition parseCacheAnnotation(AnnotatedElement ae);
} }

View File

@ -40,7 +40,7 @@ public @interface CacheEvict {
* <p>May be used to determine the target cache (or caches), matching the qualifier * <p>May be used to determine the target cache (or caches), matching the qualifier
* value (or the bean name(s)) of (a) specific bean definition. * value (or the bean name(s)) of (a) specific bean definition.
*/ */
String value() default ""; String[] value();
/** /**
* Spring Expression Language (SpEL) attribute for computing the key dynamically. * Spring Expression Language (SpEL) attribute for computing the key dynamically.
@ -57,11 +57,11 @@ public @interface CacheEvict {
String condition() default ""; String condition() default "";
/** /**
* Whether or not all the entries inside the cache are removed or not. * Whether or not all the entries inside the cache(s) are removed or not. By
* By default, only the value under the associated key is removed. * default, only the value under the associated key is removed.
* *
* Note that specifying setting this parameter to true and specifying a * Note that specifying setting this parameter to true and specifying a
* {@link CacheKey key} is not allowed. * {@link CacheKey key} is not allowed.
*/ */
boolean allEntries() default false; boolean allEntries() default false;
} }

View File

@ -37,11 +37,12 @@ import java.lang.annotation.Target;
public @interface Cacheable { public @interface Cacheable {
/** /**
* Name of the cache in which the update takes place. * Name of the caches in which the update takes place.
* <p>May be used to determine the target cache (or caches), matching the qualifier * <p>
* value (or the bean name(s)) of (a) specific bean definition. * May be used to determine the target cache (or caches), matching the
* qualifier value (or the bean name(s)) of (a) specific bean definition.
*/ */
String value() default ""; String[] value();
/** /**
* Spring Expression Language (SpEL) attribute for computing the key dynamically. * Spring Expression Language (SpEL) attribute for computing the key dynamically.

View File

@ -20,8 +20,8 @@ import java.io.Serializable;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
import org.springframework.cache.interceptor.CacheInvalidateDefinition;
import org.springframework.cache.interceptor.CacheDefinition; import org.springframework.cache.interceptor.CacheDefinition;
import org.springframework.cache.interceptor.CacheInvalidateDefinition;
import org.springframework.cache.interceptor.CacheUpdateDefinition; import org.springframework.cache.interceptor.CacheUpdateDefinition;
import org.springframework.cache.interceptor.DefaultCacheInvalidateDefinition; import org.springframework.cache.interceptor.DefaultCacheInvalidateDefinition;
import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition; import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
@ -34,17 +34,17 @@ import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class SpringCachingAnnotationParser implements CacheAnnotationParser, Serializable { public class SpringCachingAnnotationParser implements CacheAnnotationParser, Serializable {
public CacheDefinition parseTransactionAnnotation(AnnotatedElement ae) { public CacheDefinition parseCacheAnnotation(AnnotatedElement ae) {
Cacheable update = findAnnotation(ae, Cacheable.class); Cacheable update = findAnnotation(ae, Cacheable.class);
if (update != null) { if (update != null) {
return parseCacheableAnnotation(update); return parseCacheableAnnotation(ae, update);
} }
CacheEvict invalidate = findAnnotation(ae, CacheEvict.class); CacheEvict invalidate = findAnnotation(ae, CacheEvict.class);
if (invalidate != null) { if (invalidate != null) {
return parseInvalidateAnnotation(invalidate); return parseInvalidateAnnotation(ae, invalidate);
} }
return null; return null;
@ -63,22 +63,24 @@ public class SpringCachingAnnotationParser implements CacheAnnotationParser, Ser
return ann; return ann;
} }
public CacheUpdateDefinition parseCacheableAnnotation(Cacheable ann) { CacheUpdateDefinition parseCacheableAnnotation(AnnotatedElement target, Cacheable ann) {
DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition(); DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition();
dcud.setCacheName(ann.value()); dcud.setCacheNames(ann.value());
dcud.setCondition(ann.condition()); dcud.setCondition(ann.condition());
dcud.setKey(ann.key()); dcud.setKey(ann.key());
dcud.setName(target.toString());
return dcud; return dcud;
} }
public CacheInvalidateDefinition parseInvalidateAnnotation(CacheEvict ann) { CacheInvalidateDefinition parseInvalidateAnnotation(AnnotatedElement target, CacheEvict ann) {
DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition(); DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition();
dcid.setCacheName(ann.value()); dcid.setCacheNames(ann.value());
dcid.setCondition(ann.condition()); dcid.setCondition(ann.condition());
dcid.setKey(ann.key()); dcid.setKey(ann.key());
dcid.setCacheWide(ann.allEntries()); dcid.setCacheWide(ann.allEntries());
dcid.setName(target.toString());
return dcid; return dcid;
} }
} }

View File

@ -21,6 +21,7 @@ import java.util.concurrent.ConcurrentMap;
import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/** /**
* Factory bean for easy configuration of {@link ConcurrentCache} through Spring. * Factory bean for easy configuration of {@link ConcurrentCache} through Spring.
@ -52,7 +53,9 @@ public class ConcurrentCacheFactoryBean<K, V> implements FactoryBean<ConcurrentC
} }
public void setBeanName(String beanName) { public void setBeanName(String beanName) {
setName(beanName); if (!StringUtils.hasText(name)) {
setName(beanName);
}
} }
public void setName(String name) { public void setName(String name) {

View File

@ -16,6 +16,11 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.util.Assert;
/** /**
* Base class implementing {@link CacheDefinition}. * Base class implementing {@link CacheDefinition}.
@ -24,14 +29,14 @@ package org.springframework.cache.interceptor;
*/ */
abstract class AbstractCacheDefinition implements CacheDefinition { abstract class AbstractCacheDefinition implements CacheDefinition {
private String cacheName = ""; private Set<String> cacheNames = Collections.emptySet();
private String condition = ""; private String condition = "";
private String key = ""; private String key = "";
private String name = ""; private String name = "";
public String getCacheName() { public Set<String> getCacheNames() {
return cacheName; return cacheNames;
} }
public String getCondition() { public String getCondition() {
@ -47,18 +52,30 @@ abstract class AbstractCacheDefinition implements CacheDefinition {
} }
public void setCacheName(String cacheName) { public void setCacheName(String cacheName) {
this.cacheName = cacheName; Assert.hasText(cacheName);
this.cacheNames = Collections.singleton(cacheName);
}
public void setCacheNames(String[] cacheNames) {
Assert.notEmpty(cacheNames);
this.cacheNames = new LinkedHashSet<String>(cacheNames.length);
for (String string : cacheNames) {
this.cacheNames.add(string);
}
} }
public void setCondition(String condition) { public void setCondition(String condition) {
Assert.notNull(condition);
this.condition = condition; this.condition = condition;
} }
public void setKey(String key) { public void setKey(String key) {
Assert.notNull(key);
this.key = key; this.key = key;
} }
public void setName(String name) { public void setName(String name) {
Assert.hasText(name);
this.name = name; this.name = name;
} }
@ -97,12 +114,15 @@ abstract class AbstractCacheDefinition implements CacheDefinition {
*/ */
protected StringBuilder getDefinitionDescription() { protected StringBuilder getDefinitionDescription() {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
result.append(cacheName); result.append("CacheDefinition[");
result.append(','); result.append(name);
result.append("] caches=");
result.append(cacheNames);
result.append(" | condition='");
result.append(condition); result.append(condition);
result.append(","); result.append("' | key='");
result.append(key); result.append(key);
result.append("'");
return result; return result;
} }
} }

View File

@ -18,6 +18,9 @@ package org.springframework.cache.interceptor;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -58,8 +61,11 @@ import org.springframework.util.StringUtils;
public abstract class CacheAspectSupport implements InitializingBean { public abstract class CacheAspectSupport implements InitializingBean {
private static class EmptyHolder implements Serializable { private static class EmptyHolder implements Serializable {
private static final long serialVersionUID = 1L;
} }
// TODO: can null values be properly stored into user caches?
private static final Object NULL_RETURN = new EmptyHolder(); private static final Object NULL_RETURN = new EmptyHolder();
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
@ -116,21 +122,30 @@ public abstract class CacheAspectSupport implements InitializingBean {
cacheDefinitionSources) : cacheDefinitionSources[0]); cacheDefinitionSources) : cacheDefinitionSources[0]);
} }
protected <K, V> Cache<K, V> getCache(CacheDefinition definition) { protected Collection<Cache<?, ?>> getCaches(CacheDefinition definition) {
// TODO: add support for multiple caches
// TODO: add behaviour for the default cache // TODO: add behaviour for the default cache
String name = definition.getCacheName(); Set<String> cacheNames = definition.getCacheNames();
if (!StringUtils.hasText(name)) {
name = cacheManager.getCacheNames().iterator().next(); Collection<Cache<?,?>> caches = new ArrayList<Cache<?,?>>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache<Object, Object> cache = cacheManager.getCache(cacheName);
if (cache == null){
throw new IllegalArgumentException("Cannot find cache named ["+cacheName+"] for " + definition);
}
caches.add(cache);
} }
return cacheManager.getCache(name);
return caches;
} }
protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args, Class<?> targetClass) { protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args,
Class<?> targetClass) {
return new CacheOperationContext(definition, method, args, targetClass); return new CacheOperationContext(definition, method, args, targetClass);
} }
protected Object execute(Callable<Object> invocation, Object target, Method method, Object[] args) throws Exception { protected Object execute(Callable<Object> invocation, Object target,
Method method, Object[] args) throws Exception {
// get backing class // get backing class
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target); Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
@ -138,40 +153,47 @@ public abstract class CacheAspectSupport implements InitializingBean {
targetClass = target.getClass(); targetClass = target.getClass();
} }
final CacheDefinition cacheDef = getCacheDefinitionSource().getCacheDefinition(method, final CacheDefinition cacheDef = getCacheDefinitionSource()
targetClass); .getCacheDefinition(method, targetClass);
Object retVal = null; Object retVal = null;
// analyze caching information // analyze caching information
if (cacheDef != null) { if (cacheDef != null) {
CacheOperationContext context = getOperationContext(cacheDef, method, args, targetClass); CacheOperationContext context = getOperationContext(cacheDef,
Cache<Object, Object> cache = (Cache<Object, Object>) context.getCache(); method, args, targetClass);
Collection<Cache<?,?>> caches = context.getCaches();
if (context.hasConditionPassed()) { if (context.hasConditionPassed()) {
// check operation // check operation
if (cacheDef instanceof CacheUpdateDefinition) { if (cacheDef instanceof CacheUpdateDefinition) {
// for each cache
// check cache first // check cache first
Object key = context.generateKey(); for (Cache cache : caches) {
retVal = cache.get(key); Object key = context.generateKey();
if (retVal == null) { retVal = cache.get(key);
retVal = invocation.call(); if (retVal == null) {
cache.put(key, (retVal == null ? NULL_RETURN : retVal)); retVal = invocation.call();
cache.put(key, (retVal == null ? NULL_RETURN : retVal));
}
} }
} }
if (cacheDef instanceof CacheInvalidateDefinition) { if (cacheDef instanceof CacheInvalidateDefinition) {
CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef; CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef;
retVal = invocation.call(); retVal = invocation.call();
// flush the cache (ignore arguments) // for each cache
if (invalidateDef.isCacheWide()) {
cache.clear(); for (Cache cache : caches) {
} // flush the cache (ignore arguments)
else { if (invalidateDef.isCacheWide()) {
// check key cache.clear();
Object key = context.generateKey(); } else {
cache.remove(key); // check key
Object key = context.generateKey();
cache.remove(key);
}
} }
} }
@ -185,23 +207,24 @@ public abstract class CacheAspectSupport implements InitializingBean {
protected class CacheOperationContext { protected class CacheOperationContext {
private CacheDefinition definition; private CacheDefinition definition;
private final Cache<?, ?> cache; private final Collection<Cache<?, ?>> caches;
private final Method method; private final Method method;
private final Object[] args; private final Object[] args;
// context passed around to avoid multiple creations // context passed around to avoid multiple creations
private final EvaluationContext evalContext; private final EvaluationContext evalContext;
private final KeyGenerator keyGenerator = new DefaultKeyGenerator(); private final KeyGenerator<Object> keyGenerator = new DefaultKeyGenerator();
public CacheOperationContext(CacheDefinition operationDefinition, Method method, Object[] args, public CacheOperationContext(CacheDefinition operationDefinition,
Class<?> targetClass) { Method method, Object[] args, Class<?> targetClass) {
this.definition = operationDefinition; this.definition = operationDefinition;
this.cache = CacheAspectSupport.this.getCache(definition); this.caches = CacheAspectSupport.this.getCaches(definition);
this.method = method; this.method = method;
this.args = args; this.args = args;
this.evalContext = evaluator.createEvaluationContext(cache, method, args, targetClass); this.evalContext = evaluator.createEvaluationContext(caches, method,
args, targetClass);
} }
/** /**
@ -212,7 +235,8 @@ public abstract class CacheAspectSupport implements InitializingBean {
*/ */
protected boolean hasConditionPassed() { protected boolean hasConditionPassed() {
if (StringUtils.hasText(definition.getCondition())) { if (StringUtils.hasText(definition.getCondition())) {
return evaluator.condition(definition.getCondition(), method, evalContext); return evaluator.condition(definition.getCondition(), method,
evalContext);
} }
return true; return true;
} }
@ -221,8 +245,10 @@ public abstract class CacheAspectSupport implements InitializingBean {
* Computes the key for the given caching definition. * Computes the key for the given caching definition.
* *
* @param definition * @param definition
* @param method method being invoked * @param method
* @param objects arguments passed during the method invocation * method being invoked
* @param objects
* arguments passed during the method invocation
* @return generated key (null if none can be generated) * @return generated key (null if none can be generated)
*/ */
protected Object generateKey() { protected Object generateKey() {
@ -230,11 +256,11 @@ public abstract class CacheAspectSupport implements InitializingBean {
return evaluator.key(definition.getKey(), method, evalContext); return evaluator.key(definition.getKey(), method, evalContext);
} }
return keyGenerator.extract(args); return keyGenerator.extract(method, args);
} }
protected Cache<?, ?> getCache() { protected Collection<Cache<?, ?>> getCaches() {
return cache; return caches;
} }
} }
} }

View File

@ -16,6 +16,8 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.util.Set;
/** /**
* Interface describing Spring-compliant caching operation. * Interface describing Spring-compliant caching operation.
* *
@ -33,11 +35,11 @@ public interface CacheDefinition {
String getName(); String getName();
/** /**
* Returns the name of the cache against which this operation is performed. * Returns the names of the cache against which this operation is performed.
* *
* @return name of the cache on which the operation is performed. * @return names of the cache on which the operation is performed.
*/ */
String getCacheName(); Set<String> getCacheNames();
/** /**
* Returns the SpEL expression conditioning the operation. * Returns the SpEL expression conditioning the operation.

View File

@ -16,6 +16,8 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.util.Collection;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
/** /**
@ -33,9 +35,9 @@ interface CacheExpressionRootObject {
String getMethodName(); String getMethodName();
/** /**
* Returns the cache against which the method is executed. * Returns the caches against which the method is executed.
* *
* @return current cache * @return current cache
*/ */
Cache getCache(); Collection<Cache<?,?>> getCaches();
} }

View File

@ -16,6 +16,8 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.util.Collection;
import org.springframework.cache.Cache; import org.springframework.cache.Cache;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -27,19 +29,19 @@ import org.springframework.util.Assert;
public class DefaultCacheExpressionRootObject implements CacheExpressionRootObject { public class DefaultCacheExpressionRootObject implements CacheExpressionRootObject {
private final String methodName; private final String methodName;
private final Cache cache; private final Collection<Cache<?, ?>> caches;
public DefaultCacheExpressionRootObject(Cache cache, String methodName) { public DefaultCacheExpressionRootObject(Collection<Cache<?,?>> caches, String methodName) {
Assert.hasText(methodName, "method name is required"); Assert.hasText(methodName, "method name is required");
this.methodName = methodName; this.methodName = methodName;
this.cache = cache; this.caches = caches;
} }
public String getMethodName() { public String getMethodName() {
return methodName; return methodName;
} }
public Cache getCache() { public Collection<Cache<?, ?>> getCaches() {
return cache; return caches;
} }
} }

View File

@ -17,6 +17,7 @@
package org.springframework.cache.interceptor; package org.springframework.cache.interceptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -46,8 +47,8 @@ class ExpressionEvaluator {
private Map<Method, Expression> keyCache = new ConcurrentHashMap<Method, Expression>(); private Map<Method, Expression> keyCache = new ConcurrentHashMap<Method, Expression>();
private Map<Method, Method> targetMethodCache = new ConcurrentHashMap<Method, Method>(); private Map<Method, Method> targetMethodCache = new ConcurrentHashMap<Method, Method>();
EvaluationContext createEvaluationContext(Cache<?, ?> cache, Method method, Object[] args, Class<?> targetClass) { EvaluationContext createEvaluationContext(Collection<Cache<?, ?>> caches, Method method, Object[] args, Class<?> targetClass) {
DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(cache, method.getName()); DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(caches, method.getName());
StandardEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject, StandardEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject,
paramNameDiscoverer, method, args, targetClass, targetMethodCache); paramNameDiscoverer, method, args, targetClass, targetMethodCache);

View File

@ -16,14 +16,19 @@
package org.springframework.cache.support; package org.springframework.cache.support;
import java.lang.reflect.Method;
import org.springframework.cache.KeyGenerator; import org.springframework.cache.KeyGenerator;
/** /**
* Default key generator. Computes a resulting key based on the hashcode of the
* given parameters.
*
* @author Costin Leau * @author Costin Leau
*/ */
public class DefaultKeyGenerator implements KeyGenerator<Object> { public class DefaultKeyGenerator implements KeyGenerator<Object> {
public Object extract(Object... params) { public Object extract(Method method, Object... params) {
int hashCode = 17; int hashCode = 17;
for (Object object : params) { for (Object object : params) {

View File

@ -24,7 +24,7 @@ import org.springframework.cache.annotation.Cacheable;
/** /**
* @author Costin Leau * @author Costin Leau
*/ */
@Cacheable @Cacheable("default")
public class AnnotatedClassCacheableService implements CacheableService { public class AnnotatedClassCacheableService implements CacheableService {
private AtomicLong counter = new AtomicLong(); private AtomicLong counter = new AtomicLong();
@ -37,11 +37,11 @@ public class AnnotatedClassCacheableService implements CacheableService {
return null; return null;
} }
@CacheEvict @CacheEvict("default")
public void invalidate(Object arg1) { public void invalidate(Object arg1) {
} }
@Cacheable(key = "#p0") @Cacheable(value = "default", key = "#p0")
public Object key(Object arg1, Object arg2) { public Object key(Object arg1, Object arg2) {
return counter.getAndIncrement(); return counter.getAndIncrement();
} }

View File

@ -31,21 +31,21 @@ public class DefaultCacheableService implements CacheableService<Long> {
private AtomicLong counter = new AtomicLong(); private AtomicLong counter = new AtomicLong();
@Cacheable @Cacheable("default")
public Long cache(Object arg1) { public Long cache(Object arg1) {
return counter.getAndIncrement(); return counter.getAndIncrement();
} }
@CacheEvict @CacheEvict("default")
public void invalidate(Object arg1) { public void invalidate(Object arg1) {
} }
@Cacheable(condition = "#classField == 3") @Cacheable(value = "default", condition = "#classField == 3")
public Long conditional(int classField) { public Long conditional(int classField) {
return counter.getAndIncrement(); return counter.getAndIncrement();
} }
@Cacheable(key = "#p0") @Cacheable(value = "default", key = "#p0")
public Long key(Object arg1, Object arg2) { public Long key(Object arg1, Object arg2) {
return counter.getAndIncrement(); return counter.getAndIncrement();
} }