revise cache API
- eliminate unneeded methods + introduced value wrapper (name still to be decided) to avoid cache race conditions + improved name consistency
This commit is contained in:
parent
af1dfd3577
commit
0b917e3f9c
|
|
@ -22,7 +22,7 @@ import java.util.concurrent.Callable;
|
|||
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.cache.interceptor.CacheAspectSupport;
|
||||
import org.springframework.cache.interceptor.CacheDefinitionSource;
|
||||
import org.springframework.cache.interceptor.CacheOperationSource;
|
||||
|
||||
/**
|
||||
* Abstract superaspect for AspectJ cache aspects. Concrete
|
||||
|
|
@ -46,11 +46,11 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
|||
|
||||
/**
|
||||
* Construct object using the given caching metadata retrieval strategy.
|
||||
* @param cds {@link CacheDefinitionSource} implementation, retrieving Spring
|
||||
* @param cos {@link CacheOperationSource} implementation, retrieving Spring
|
||||
* cache metadata for each joinpoint.
|
||||
*/
|
||||
protected AbstractCacheAspect(CacheDefinitionSource... cds) {
|
||||
setCacheDefinitionSources(cds);
|
||||
protected AbstractCacheAspect(CacheOperationSource... cos) {
|
||||
setCacheDefinitionSources(cos);
|
||||
}
|
||||
|
||||
@SuppressAjWarnings("adviceDidNotMatch")
|
||||
|
|
@ -75,7 +75,7 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
|||
/**
|
||||
* Concrete subaspects must implement this pointcut, to identify
|
||||
* cached methods. For each selected joinpoint, {@link CacheOperationDefinition}
|
||||
* will be retrieved using Spring's {@link CacheDefinitionSource} interface.
|
||||
* will be retrieved using Spring's {@link CacheOperationSource} interface.
|
||||
*/
|
||||
protected abstract pointcut cacheMethodExecution(Object cachedObject);
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.springframework.cache.aspectj;
|
||||
|
||||
import org.springframework.cache.annotation.AnnotationCacheDefinitionSource;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
|
|
@ -43,7 +43,7 @@ import org.springframework.cache.annotation.Cacheable;
|
|||
public aspect AnnotationCacheAspect extends AbstractCacheAspect {
|
||||
|
||||
public AnnotationCacheAspect() {
|
||||
super(new AnnotationCacheDefinitionSource(false));
|
||||
super(new AnnotationCacheOperationSource(false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,6 +28,18 @@ package org.springframework.cache;
|
|||
*/
|
||||
public interface Cache<K, V> {
|
||||
|
||||
/**
|
||||
* A (wrapper) object representing a cache value.
|
||||
*/
|
||||
interface ValueWrapper<V> {
|
||||
/**
|
||||
* Returns the actual value in the cache.
|
||||
*
|
||||
* @return cache value
|
||||
*/
|
||||
V get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cache name.
|
||||
*
|
||||
|
|
@ -38,164 +50,36 @@ public interface Cache<K, V> {
|
|||
/**
|
||||
* Returns the the native, underlying cache provider.
|
||||
*
|
||||
* @return
|
||||
* @return the underlying native cache provider.
|
||||
*/
|
||||
Object getNativeCache();
|
||||
|
||||
/**
|
||||
* Returns <tt>true</tt> if this cache contains a mapping for the specified
|
||||
* key. More formally, returns <tt>true</tt> if and only if
|
||||
* this cache contains a mapping for a key <tt>k</tt> such that
|
||||
* <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be
|
||||
* at most one such mapping.)
|
||||
*
|
||||
* @param key key whose presence in this cache is to be tested.
|
||||
* @return <tt>true</tt> if this cache contains a mapping for the specified
|
||||
* key.
|
||||
*/
|
||||
boolean containsKey(Object key);
|
||||
|
||||
/**
|
||||
* Returns the value to which this cache maps the specified key. Returns
|
||||
* <tt>null</tt> if the cache contains no mapping for this key. A return
|
||||
* value of <tt>null</tt> does not <i>necessarily</i> indicate that the
|
||||
* cache contains no mapping for the key; it's also possible that the cache
|
||||
* explicitly maps the key to <tt>null</tt>. The <tt>containsKey</tt>
|
||||
* operation may be used to distinguish these two cases.
|
||||
*
|
||||
* <p>More formally, if this cache contains a mapping from a key
|
||||
* <tt>k</tt> to a value <tt>v</tt> such that <tt>(key==null ? k==null :
|
||||
* key.equals(k))</tt>, then this method returns <tt>v</tt>; otherwise
|
||||
* it returns <tt>null</tt>. (There can be at most one such mapping.)
|
||||
*
|
||||
* Returns the value to which this cache maps the specified key. Returns
|
||||
* <tt>null</tt> if the cache contains no mapping for this key.
|
||||
*
|
||||
* @param key key whose associated value is to be returned.
|
||||
* @return the value to which this cache maps the specified key, or
|
||||
* <tt>null</tt> if the cache contains no mapping for this key.
|
||||
*
|
||||
* @see #containsKey(Object)
|
||||
*/
|
||||
V get(Object key);
|
||||
|
||||
ValueWrapper<V> get(Object key);
|
||||
|
||||
/**
|
||||
* Associates the specified value with the specified key in this cache
|
||||
* (optional operation). If the cache previously contained a mapping for
|
||||
* this key, the old value is replaced by the specified value. (A cache
|
||||
* <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
|
||||
* if {@link #containsKey(Object) m.containsKey(k)} would return
|
||||
* <tt>true</tt>.))
|
||||
* Associates the specified value with the specified key in this cache.
|
||||
* If the cache previously contained a mapping for this key, the old
|
||||
* value is replaced by the specified value.
|
||||
*
|
||||
* @param key key with which the specified value is to be associated.
|
||||
* @param value value to be associated with the specified key.
|
||||
* @return previous value associated with specified key, or <tt>null</tt>
|
||||
* if there was no mapping for key. A <tt>null</tt> return can
|
||||
* also indicate that the cache previously associated <tt>null</tt>
|
||||
* with the specified key, if the implementation supports
|
||||
* <tt>null</tt> values.
|
||||
*/
|
||||
V put(K key, V value);
|
||||
|
||||
void put(K key, V value);
|
||||
|
||||
/**
|
||||
* If the specified key is not already associated with a value, associate it with the given value.
|
||||
*
|
||||
* This is equivalent to:
|
||||
* <pre>
|
||||
* if (!cache.containsKey(key))
|
||||
* return cache.put(key, value);
|
||||
* else
|
||||
* return cache.get(key);
|
||||
* </pre>
|
||||
*
|
||||
* @param key key with which the specified value is to be associated.
|
||||
* @param value value to be associated with the specified key.
|
||||
* @return previous value associated with specified key, or <tt>null</tt>
|
||||
* if there was no mapping for key. A <tt>null</tt> return can
|
||||
* also indicate that the cache previously associated <tt>null</tt>
|
||||
* with the specified key, if the implementation supports
|
||||
* <tt>null</tt> values.
|
||||
*/
|
||||
V putIfAbsent(K key, V value);
|
||||
|
||||
|
||||
/**
|
||||
* Removes the mapping for this key from this cache if it is present
|
||||
* (optional operation). More formally, if this cache contains a mapping
|
||||
* from key <tt>k</tt> to value <tt>v</tt> such that
|
||||
* <code>(key==null ? k==null : key.equals(k))</code>, that mapping
|
||||
* is removed. (The cache can contain at most one such mapping.)
|
||||
*
|
||||
* <p>Returns the value to which the cache previously associated the key, or
|
||||
* <tt>null</tt> if the cache contained no mapping for this key. (A
|
||||
* <tt>null</tt> return can also indicate that the cache previously
|
||||
* associated <tt>null</tt> with the specified key if the implementation
|
||||
* supports <tt>null</tt> values.) The cache will not contain a mapping for
|
||||
* the specified key once the call returns.
|
||||
* Evicts the mapping for this key from this cache if it is present.
|
||||
*
|
||||
* @param key key whose mapping is to be removed from the cache.
|
||||
* @return previous value associated with specified key, or <tt>null</tt>
|
||||
* if there was no mapping for key.
|
||||
*/
|
||||
V remove(Object key);
|
||||
|
||||
|
||||
/**
|
||||
* Remove entry for key only if currently mapped to given value.
|
||||
*
|
||||
* Similar to:
|
||||
* <pre>
|
||||
* if ((cache.containsKey(key) && cache.get(key).equals(value)) {
|
||||
* cache.remove(key);
|
||||
* return true;
|
||||
* }
|
||||
* else
|
||||
* return false;
|
||||
* </pre>
|
||||
*
|
||||
* @param key key with which the specified value is associated.
|
||||
* @param value value associated with the specified key.
|
||||
* @return true if the value was removed, false otherwise
|
||||
*/
|
||||
boolean remove(Object key, Object value);
|
||||
|
||||
|
||||
/**
|
||||
* Replace entry for key only if currently mapped to given value.
|
||||
*
|
||||
* Similar to:
|
||||
* <pre>
|
||||
* if ((cache.containsKey(key) && cache.get(key).equals(oldValue)) {
|
||||
* cache.put(key, newValue);
|
||||
* return true;
|
||||
* } else return false;
|
||||
* </pre>
|
||||
|
||||
* @param key key with which the specified value is associated.
|
||||
* @param oldValue value expected to be associated with the specified key.
|
||||
* @param newValue value to be associated with the specified key.
|
||||
* @return true if the value was replaced
|
||||
*/
|
||||
boolean replace(K key, V oldValue, V newValue);
|
||||
|
||||
/**
|
||||
* Replace entry for key only if currently mapped to some value.
|
||||
* Acts as
|
||||
* <pre>
|
||||
* if ((cache.containsKey(key)) {
|
||||
* return cache.put(key, value);
|
||||
* } else return null;
|
||||
* </pre>
|
||||
* except that the action is performed atomically.
|
||||
* @param key key with which the specified value is associated.
|
||||
* @param value value to be associated with the specified key.
|
||||
* @return previous value associated with specified key, or <tt>null</tt>
|
||||
* if there was no mapping for key. A <tt>null</tt> return can
|
||||
* also indicate that the cache previously associated <tt>null</tt>
|
||||
* with the specified key, if the implementation supports
|
||||
* <tt>null</tt> values.
|
||||
*/
|
||||
V replace(K key, V value);
|
||||
|
||||
void evict(Object key);
|
||||
|
||||
/**
|
||||
* Removes all mappings from the cache.
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.cache.interceptor.AbstractFallbackCacheDefinitionSource;
|
||||
import org.springframework.cache.interceptor.CacheDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
*
|
||||
* Implementation of the
|
||||
* {@link org.springframework.cache.interceptor.CacheDefinitionSource}
|
||||
* interface for working with caching metadata in JDK 1.5+ annotation format.
|
||||
*
|
||||
* <p>This class reads Spring's JDK 1.5+ {@link Cacheable} and {@link CacheEvict}
|
||||
* annotations and
|
||||
* exposes corresponding caching operation definition to Spring's cache infrastructure.
|
||||
* This class may also serve as base class for a custom CacheDefinitionSource.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AnnotationCacheDefinitionSource extends AbstractFallbackCacheDefinitionSource implements
|
||||
Serializable {
|
||||
|
||||
private final boolean publicMethodsOnly;
|
||||
|
||||
private final Set<CacheAnnotationParser> annotationParsers;
|
||||
|
||||
/**
|
||||
* Create a default AnnotationCacheOperationDefinitionSource, supporting
|
||||
* public methods that carry the <code>Cacheable</code> and <code>CacheInvalidate</code>
|
||||
* annotations.
|
||||
*/
|
||||
public AnnotationCacheDefinitionSource() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationDefinitionSource, supporting
|
||||
* public methods that carry the <code>Cacheable</code> and
|
||||
* <code>CacheInvalidate</code> annotations.
|
||||
*
|
||||
* @param publicMethodsOnly whether to support only annotated public methods
|
||||
* typically for use with proxy-based AOP), or protected/private methods as well
|
||||
* (typically used with AspectJ class weaving)
|
||||
*/
|
||||
public AnnotationCacheDefinitionSource(boolean publicMethodsOnly) {
|
||||
this.publicMethodsOnly = publicMethodsOnly;
|
||||
this.annotationParsers = new LinkedHashSet<CacheAnnotationParser>(1);
|
||||
this.annotationParsers.add(new SpringCachingAnnotationParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationDefinitionSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheDefinitionSource(CacheAnnotationParser... annotationParsers) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
|
||||
Set<CacheAnnotationParser> parsers = new LinkedHashSet<CacheAnnotationParser>(annotationParsers.length);
|
||||
Collections.addAll(parsers, annotationParsers);
|
||||
this.annotationParsers = parsers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheDefinition findCacheDefinition(Class<?> clazz) {
|
||||
return determineCacheDefinition(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CacheDefinition findCacheOperation(Method method) {
|
||||
return determineCacheDefinition(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the cache operation definition for the given method or class.
|
||||
* <p>This implementation delegates to configured
|
||||
* {@link CacheAnnotationParser CacheAnnotationParsers}
|
||||
* for parsing known annotations into Spring's metadata attribute class.
|
||||
* Returns <code>null</code> if it's not cacheable.
|
||||
* <p>Can be overridden to support custom annotations that carry caching metadata.
|
||||
* @param ae the annotated method or class
|
||||
* @return CacheOperationDefinition the configured caching operation,
|
||||
* or <code>null</code> if none was found
|
||||
*/
|
||||
protected CacheDefinition determineCacheDefinition(AnnotatedElement ae) {
|
||||
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
|
||||
CacheDefinition attr = annotationParser.parseCacheAnnotation(ae);
|
||||
if (attr != null) {
|
||||
return attr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, only public methods can be made cacheable.
|
||||
*/
|
||||
@Override
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return this.publicMethodsOnly;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ package org.springframework.cache.annotation;
|
|||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheDefinition;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -38,9 +38,9 @@ public interface CacheAnnotationParser {
|
|||
* metadata attribute class. Returns <code>null</code> if the method/class
|
||||
* is not cacheable.
|
||||
* @param ae the annotated method or class
|
||||
* @return CacheOperationDefinition the configured caching operation,
|
||||
* @return CacheOperation the configured caching operation,
|
||||
* or <code>null</code> if none was found
|
||||
* @see AnnotationCacheDefinitionSource#determineCacheOperationDefinition
|
||||
* @see AnnotationCacheOperationSource#determineCacheOperation
|
||||
*/
|
||||
CacheDefinition parseCacheAnnotation(AnnotatedElement ae);
|
||||
CacheOperation parseCacheAnnotation(AnnotatedElement ae);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ import java.io.Serializable;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheDefinition;
|
||||
import org.springframework.cache.interceptor.CacheInvalidateDefinition;
|
||||
import org.springframework.cache.interceptor.CacheUpdateDefinition;
|
||||
import org.springframework.cache.interceptor.DefaultCacheInvalidateDefinition;
|
||||
import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
|
||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheUpdateOperation;
|
||||
|
||||
/**
|
||||
* Strategy implementation for parsing Spring's {@link Cacheable} and {@link CacheEvict} annotations.
|
||||
|
|
@ -34,7 +32,7 @@ import org.springframework.cache.interceptor.DefaultCacheUpdateDefinition;
|
|||
@SuppressWarnings("serial")
|
||||
public class SpringCachingAnnotationParser implements CacheAnnotationParser, Serializable {
|
||||
|
||||
public CacheDefinition parseCacheAnnotation(AnnotatedElement ae) {
|
||||
public CacheOperation parseCacheAnnotation(AnnotatedElement ae) {
|
||||
Cacheable update = findAnnotation(ae, Cacheable.class);
|
||||
|
||||
if (update != null) {
|
||||
|
|
@ -44,7 +42,7 @@ public class SpringCachingAnnotationParser implements CacheAnnotationParser, Ser
|
|||
CacheEvict invalidate = findAnnotation(ae, CacheEvict.class);
|
||||
|
||||
if (invalidate != null) {
|
||||
return parseInvalidateAnnotation(ae, invalidate);
|
||||
return parseEvictAnnotation(ae, invalidate);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
@ -63,8 +61,8 @@ public class SpringCachingAnnotationParser implements CacheAnnotationParser, Ser
|
|||
return ann;
|
||||
}
|
||||
|
||||
CacheUpdateDefinition parseCacheableAnnotation(AnnotatedElement target, Cacheable ann) {
|
||||
DefaultCacheUpdateDefinition dcud = new DefaultCacheUpdateDefinition();
|
||||
CacheUpdateOperation parseCacheableAnnotation(AnnotatedElement target, Cacheable ann) {
|
||||
CacheUpdateOperation dcud = new CacheUpdateOperation();
|
||||
dcud.setCacheNames(ann.value());
|
||||
dcud.setCondition(ann.condition());
|
||||
dcud.setKey(ann.key());
|
||||
|
|
@ -73,8 +71,8 @@ public class SpringCachingAnnotationParser implements CacheAnnotationParser, Ser
|
|||
return dcud;
|
||||
}
|
||||
|
||||
CacheInvalidateDefinition parseInvalidateAnnotation(AnnotatedElement target, CacheEvict ann) {
|
||||
DefaultCacheInvalidateDefinition dcid = new DefaultCacheInvalidateDefinition();
|
||||
CacheEvictOperation parseEvictAnnotation(AnnotatedElement target, CacheEvict ann) {
|
||||
CacheEvictOperation dcid = new CacheEvictOperation();
|
||||
dcid.setCacheNames(ann.value());
|
||||
dcid.setCondition(ann.condition());
|
||||
dcid.setKey(ann.key());
|
||||
|
|
|
|||
|
|
@ -54,52 +54,4 @@ public class ConcurrentCache<K, V> extends AbstractDelegatingCache<K, V> {
|
|||
public ConcurrentMap<K, V> getNativeCache() {
|
||||
return store;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public V putIfAbsent(K key, V value) {
|
||||
if (getAllowNullValues()) {
|
||||
if (value == null) {
|
||||
ConcurrentMap raw = store;
|
||||
return filterNull((V) raw.putIfAbsent(key, NULL_HOLDER));
|
||||
}
|
||||
}
|
||||
return filterNull(store.putIfAbsent(key, value));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean remove(Object key, Object value) {
|
||||
if (getAllowNullValues()) {
|
||||
if (value == null) {
|
||||
ConcurrentMap raw = store;
|
||||
return raw.remove(key, NULL_HOLDER);
|
||||
}
|
||||
}
|
||||
|
||||
return store.remove(key, value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean replace(K key, V oldValue, V newValue) {
|
||||
if (getAllowNullValues()) {
|
||||
Object rawOldValue = (oldValue == null ? NULL_HOLDER : oldValue);
|
||||
Object rawNewValue = (newValue == null ? NULL_HOLDER : newValue);
|
||||
|
||||
ConcurrentMap raw = store;
|
||||
return raw.replace(key, rawOldValue, rawNewValue);
|
||||
}
|
||||
|
||||
return store.replace(key, oldValue, newValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public V replace(K key, V value) {
|
||||
if (getAllowNullValues()) {
|
||||
if (value == null) {
|
||||
ConcurrentMap raw = store;
|
||||
return filterNull((V) raw.replace(key, NULL_HOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
return filterNull(store.replace(key, value));
|
||||
}
|
||||
}
|
||||
|
|
@ -16,9 +16,7 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME;
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.CACHE_ASPECT_BEAN_NAME;
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.CACHE_ASPECT_CLASS_NAME;
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.*;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
|
|
@ -28,8 +26,8 @@ import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
|||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.cache.annotation.AnnotationCacheDefinitionSource;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheDefinitionSourceAdvisor;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
|
@ -114,7 +112,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
// Create the CacheDefinitionSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationCacheDefinitionSource.class);
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationCacheOperationSource.class);
|
||||
sourceDef.setSource(eleSource);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
|
|
@ -128,7 +126,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheDefinitionSourceAdvisor.class);
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class);
|
||||
advisorDef.setSource(eleSource);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("cacheDefinitionSource", new RuntimeBeanReference(sourceName));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import net.sf.ehcache.Element;
|
|||
import net.sf.ehcache.Status;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.DefaultValue;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
|
@ -57,77 +58,16 @@ public class EhCacheCache implements Cache<Object, Object> {
|
|||
cache.removeAll();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
// get the element to force the expiry check (since #isKeyInCache does not considers that)
|
||||
Element element = cache.getQuiet(key);
|
||||
return (element != null ? true : false);
|
||||
}
|
||||
|
||||
public Object get(Object key) {
|
||||
public ValueWrapper<Object> get(Object key) {
|
||||
Element element = cache.get(key);
|
||||
return (element != null ? element.getObjectValue() : null);
|
||||
return (element != null ? new DefaultValue<Object>(element.getObjectValue()) : null);
|
||||
}
|
||||
|
||||
public Object put(Object key, Object value) {
|
||||
Element previous = cache.getQuiet(key);
|
||||
public void put(Object key, Object value) {
|
||||
cache.put(new Element(key, value));
|
||||
return (previous != null ? previous.getValue() : null);
|
||||
}
|
||||
|
||||
public Object remove(Object key) {
|
||||
Element element = cache.getQuiet(key);
|
||||
Object value = (element != null ? element.getObjectValue() : null);
|
||||
public void evict(Object key) {
|
||||
cache.remove(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
public Object putIfAbsent(Object key, Object value) {
|
||||
// putIfAbsent supported only from Ehcache 2.1
|
||||
// return cache.putIfAbsent(new Element(key, value));
|
||||
Element existing = cache.getQuiet(key);
|
||||
if (existing == null) {
|
||||
cache.put(new Element(key, value));
|
||||
return null;
|
||||
}
|
||||
return existing.getObjectValue();
|
||||
}
|
||||
|
||||
public boolean remove(Object key, Object value) {
|
||||
// remove(Element) supported only from Ehcache 2.1
|
||||
// return cache.removeElement(new Element(key, value));
|
||||
Element existing = cache.getQuiet(key);
|
||||
|
||||
if (existing != null && existing.getObjectValue().equals(value)) {
|
||||
cache.remove(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object replace(Object key, Object value) {
|
||||
// replace(Object, Object) supported only from Ehcache 2.1
|
||||
// return cache.replace(new Element(key, value));
|
||||
Element existing = cache.getQuiet(key);
|
||||
|
||||
if (existing != null) {
|
||||
cache.put(new Element(key, value));
|
||||
return existing.getObjectValue();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean replace(Object key, Object oldValue, Object newValue) {
|
||||
// replace(Object, Object, Object) supported only from Ehcache 2.1
|
||||
// return cache.replace(new Element(key, oldValue), new Element(key,
|
||||
// newValue));
|
||||
Element existing = cache.getQuiet(key);
|
||||
|
||||
if (existing != null && existing.getObjectValue().equals(oldValue)) {
|
||||
cache.put(new Element(key, newValue));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
abstract class AbstractCacheDefinition implements CacheDefinition {
|
||||
|
||||
private Set<String> cacheNames = Collections.emptySet();
|
||||
private String condition = "";
|
||||
private String key = "";
|
||||
private String name = "";
|
||||
|
||||
|
||||
public Set<String> getCacheNames() {
|
||||
return cacheNames;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setCacheName(String 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) {
|
||||
Assert.notNull(condition);
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
Assert.notNull(key);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
Assert.hasText(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation compares the <code>toString()</code> results.
|
||||
* @see #toString()
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (other instanceof CacheDefinition && toString().equals(other.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns <code>toString()</code>'s hash code.
|
||||
* @see #toString()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifying description for this cache operation definition.
|
||||
* <p>Has to be overridden in subclasses for correct <code>equals</code>
|
||||
* and <code>hashCode</code> behavior. Alternatively, {@link #equals}
|
||||
* and {@link #hashCode} can be overridden themselves.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getDefinitionDescription().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifying description for this caching definition.
|
||||
* <p>Available to subclasses, for inclusion in their <code>toString()</code> result.
|
||||
*/
|
||||
protected StringBuilder getDefinitionDescription() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append("CacheDefinition[");
|
||||
result.append(name);
|
||||
result.append("] caches=");
|
||||
result.append(cacheNames);
|
||||
result.append(" | condition='");
|
||||
result.append(condition);
|
||||
result.append("' | key='");
|
||||
result.append(key);
|
||||
result.append("'");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CacheDefinition} that caches
|
||||
* attributes for methods and implements a fallback policy: 1. specific target
|
||||
* method; 2. target class; 3. declaring method; 4. declaring class/interface.
|
||||
*
|
||||
* <p>Defaults to using the target class's caching attribute if none is
|
||||
* associated with the target method. Any caching attribute associated with
|
||||
* the target method completely overrides a class caching attribute.
|
||||
* If none found on the target class, the interface that the invoked method
|
||||
* has been called through (in case of a JDK proxy) will be checked.
|
||||
*
|
||||
* <p>This implementation caches attributes by method after they are first used.
|
||||
* If it is ever desirable to allow dynamic changing of cacheable attributes
|
||||
* (which is very unlikely), caching could be made configurable.
|
||||
|
||||
* @author Costin Leau
|
||||
* @see org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource
|
||||
*/
|
||||
public abstract class AbstractFallbackCacheDefinitionSource implements CacheDefinitionSource {
|
||||
|
||||
/**
|
||||
* Canonical value held in cache to indicate no caching attribute was
|
||||
* found for this method, and we don't need to look again.
|
||||
*/
|
||||
private final static CacheDefinition NULL_CACHING_ATTRIBUTE = new DefaultCacheUpdateDefinition();
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
* <p>As this base class is not marked Serializable, the logger will be recreated
|
||||
* after serialization - provided that the concrete subclass is Serializable.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Cache of CacheOperationDefinitions, keyed by DefaultCacheKey (Method + target Class).
|
||||
* <p>As this base class is not marked Serializable, the cache will be recreated
|
||||
* after serialization - provided that the concrete subclass is Serializable.
|
||||
*/
|
||||
final Map<Object, CacheDefinition> attributeCache = new ConcurrentHashMap<Object, CacheDefinition>();
|
||||
|
||||
/**
|
||||
* Determine the caching attribute for this method invocation.
|
||||
* <p>Defaults to the class's caching attribute if no method attribute is found.
|
||||
* @param method the method for the current invocation (never <code>null</code>)
|
||||
* @param targetClass the target class for this invocation (may be <code>null</code>)
|
||||
* @return {@link CacheDefinition} for this method, or <code>null</code> if the method
|
||||
* is not cacheable
|
||||
*/
|
||||
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
|
||||
// First, see if we have a cached value.
|
||||
Object cacheKey = getCacheKey(method, targetClass);
|
||||
CacheDefinition cached = this.attributeCache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
if (cached == NULL_CACHING_ATTRIBUTE) {
|
||||
return null;
|
||||
}
|
||||
// Value will either be canonical value indicating there is no caching attribute,
|
||||
// or an actual caching attribute.
|
||||
return cached;
|
||||
}
|
||||
else {
|
||||
// We need to work it out.
|
||||
CacheDefinition cacheDef = computeCacheOperationDefinition(method, targetClass);
|
||||
// Put it in the cache.
|
||||
if (cacheDef == null) {
|
||||
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheDef);
|
||||
}
|
||||
this.attributeCache.put(cacheKey, cacheDef);
|
||||
}
|
||||
return cacheDef;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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</code>)
|
||||
* @param targetClass the target class (may be <code>null</code>)
|
||||
* @return the cache key (never <code>null</code>)
|
||||
*/
|
||||
protected Object getCacheKey(Method method, Class<?> targetClass) {
|
||||
return new DefaultCacheKey(method, targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
|
||||
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
|
||||
* @see #getTransactionAttribute
|
||||
*/
|
||||
private CacheDefinition computeCacheOperationDefinition(Method method, Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
// If we are dealing with method with generic parameters, find the original method.
|
||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
|
||||
// First try is the method in the target class.
|
||||
CacheDefinition opDef = findCacheOperation(specificMethod);
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
|
||||
// Second try is the caching operation on the target class.
|
||||
opDef = findCacheDefinition(specificMethod.getDeclaringClass());
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
|
||||
if (specificMethod != method) {
|
||||
// Fall back is to look at the original method.
|
||||
opDef = findCacheOperation(method);
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
// Last fall back is the class of the original method.
|
||||
return findCacheDefinition(method.getDeclaringClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching attribute
|
||||
* for the given method, if any.
|
||||
* @param method the method to retrieve the attribute for
|
||||
* @return all caching attribute associated with this method
|
||||
* (or <code>null</code> if none)
|
||||
*/
|
||||
protected abstract CacheDefinition findCacheOperation(Method method);
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching attribute
|
||||
* for the given class, if any.
|
||||
* @param clazz the class to retrieve the attribute for
|
||||
* @return all caching attribute associated with this class
|
||||
* (or <code>null</code> if none)
|
||||
*/
|
||||
protected abstract CacheDefinition findCacheDefinition(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Should only public methods be allowed to have caching semantics?
|
||||
* <p>The default implementation returns <code>false</code>.
|
||||
*/
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default cache key for the CacheOperationDefinition cache.
|
||||
*/
|
||||
private static class DefaultCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
public DefaultCacheKey(Method method, Class<?> targetClass) {
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof DefaultCacheKey)) {
|
||||
return false;
|
||||
}
|
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other;
|
||||
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
|
||||
otherKey.targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Advisor driven by a {@link CacheDefinitionSource}, used to include a
|
||||
* transaction advice bean for methods that are transactional.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BeanFactoryCacheDefinitionSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||
|
||||
private CacheDefinitionSource cacheDefinitionSource;
|
||||
|
||||
private final CacheDefinitionSourcePointcut pointcut = new CacheDefinitionSourcePointcut() {
|
||||
@Override
|
||||
protected CacheDefinitionSource getCacheDefinitionSource() {
|
||||
return cacheDefinitionSource;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @see CacheInterceptor#setCacheAttributeSource
|
||||
*/
|
||||
public void setCacheDefinitionSource(CacheDefinitionSource cacheDefinitionSource) {
|
||||
this.cacheDefinitionSource = cacheDefinitionSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ClassFilter} to use for this pointcut.
|
||||
* Default is {@link ClassFilter#TRUE}.
|
||||
*/
|
||||
public void setClassFilter(ClassFilter classFilter) {
|
||||
this.pointcut.setClassFilter(classFilter);
|
||||
}
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private CacheDefinitionSource cacheDefinitionSource;
|
||||
private CacheOperationSource cacheDefinitionSource;
|
||||
|
||||
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
public CacheDefinitionSource getCacheDefinitionSource() {
|
||||
public CacheOperationSource getCacheDefinitionSource() {
|
||||
return cacheDefinitionSource;
|
||||
}
|
||||
|
||||
|
|
@ -118,21 +118,21 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
* Set multiple cache definition sources which are used to find the cache
|
||||
* attributes. Will build a CompositeCachingDefinitionSource for the given sources.
|
||||
*/
|
||||
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
|
||||
public void setCacheDefinitionSources(CacheOperationSource... cacheDefinitionSources) {
|
||||
Assert.notEmpty(cacheDefinitionSources);
|
||||
this.cacheDefinitionSource = (cacheDefinitionSources.length > 1 ? new CompositeCacheDefinitionSource(
|
||||
this.cacheDefinitionSource = (cacheDefinitionSources.length > 1 ? new CompositeCacheOperationSource(
|
||||
cacheDefinitionSources) : cacheDefinitionSources[0]);
|
||||
}
|
||||
|
||||
protected Collection<Cache<?, ?>> getCaches(CacheDefinition definition) {
|
||||
Set<String> cacheNames = definition.getCacheNames();
|
||||
protected Collection<Cache<?, ?>> getCaches(CacheOperation operation) {
|
||||
Set<String> cacheNames = operation.getCacheNames();
|
||||
|
||||
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);
|
||||
throw new IllegalArgumentException("Cannot find cache named [" + cacheName + "] for " + operation);
|
||||
}
|
||||
caches.add(cache);
|
||||
}
|
||||
|
|
@ -140,9 +140,9 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
return caches;
|
||||
}
|
||||
|
||||
protected CacheOperationContext getOperationContext(CacheDefinition definition, Method method, Object[] args,
|
||||
protected CacheOperationContext getOperationContext(CacheOperation operation, Method method, Object[] args,
|
||||
Object target, Class<?> targetClass) {
|
||||
return new CacheOperationContext(definition, method, args, target, targetClass);
|
||||
return new CacheOperationContext(operation, method, args, target, targetClass);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -156,112 +156,63 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
boolean log = logger.isTraceEnabled();
|
||||
|
||||
final CacheDefinition cacheDef = getCacheDefinitionSource().getCacheDefinition(method, targetClass);
|
||||
final CacheOperation cacheOp = getCacheDefinitionSource().getCacheOperation(method, targetClass);
|
||||
|
||||
Object retVal = null;
|
||||
|
||||
// analyze caching information
|
||||
if (cacheDef != null) {
|
||||
CacheOperationContext context = getOperationContext(cacheDef, method, args, target, targetClass);
|
||||
if (cacheOp != null) {
|
||||
CacheOperationContext context = getOperationContext(cacheOp, method, args, target, targetClass);
|
||||
Collection<Cache<?, ?>> caches = context.getCaches();
|
||||
|
||||
if (context.hasConditionPassed()) {
|
||||
// check operation
|
||||
if (cacheDef instanceof CacheUpdateDefinition) {
|
||||
if (cacheOp instanceof CacheUpdateOperation) {
|
||||
Object key = context.generateKey();
|
||||
|
||||
if (log) {
|
||||
logger.trace("Computed cache key " + key + " for definition " + cacheDef);
|
||||
logger.trace("Computed cache key " + key + " for definition " + cacheOp);
|
||||
}
|
||||
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Null key returned for cache definition (maybe you are using named params on classes without debug info?) "
|
||||
+ cacheDef);
|
||||
+ cacheOp);
|
||||
}
|
||||
|
||||
//
|
||||
// check usage of single cache
|
||||
// very common case which allows for some optimization
|
||||
// in exchange for code readability
|
||||
//
|
||||
// for each cache
|
||||
boolean cacheHit = false;
|
||||
|
||||
if (caches.size() == 1) {
|
||||
Cache cache = caches.iterator().next();
|
||||
for (Iterator<Cache<?, ?>> iterator = caches.iterator(); iterator.hasNext() && !cacheHit;) {
|
||||
Cache cache = iterator.next();
|
||||
Cache.ValueWrapper<Object> value = cache.get(key);
|
||||
|
||||
// always get the value
|
||||
retVal = cache.get(key);
|
||||
// to avoid race-conditions of entries being removed between contains/get calls
|
||||
if (cache.containsKey(key)) {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " found in cache, returning value " + retVal);
|
||||
}
|
||||
return retVal;
|
||||
} else {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " NOT found in cache, invoking target method for caching "
|
||||
+ method);
|
||||
}
|
||||
|
||||
retVal = invocation.call();
|
||||
cache.put(key, retVal);
|
||||
if (value != null) {
|
||||
cacheHit = true;
|
||||
retVal = value.get();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// multi cache path
|
||||
//
|
||||
else {
|
||||
// to avoid the contains/get race condition we can:
|
||||
// a. get the value in advanced (aka 'eagerGet')
|
||||
// b. double check 'contains' before and after get
|
||||
// a implies more calls in total if more then 3 caches are used (n*2 calls)
|
||||
// b uses less calls in total but is 1 call heavier for one cache (n+2 calls)
|
||||
// --
|
||||
// for balance, a) is used for up to 3 caches, b for more then 4
|
||||
|
||||
boolean eagerGet = caches.size() <= 3;
|
||||
|
||||
// for each cache
|
||||
boolean cacheHit = false;
|
||||
|
||||
for (Iterator<Cache<?, ?>> iterator = caches.iterator(); iterator.hasNext() && !cacheHit;) {
|
||||
Cache cache = iterator.next();
|
||||
if (eagerGet) {
|
||||
retVal = cache.get(key);
|
||||
}
|
||||
if (cache.containsKey(key)) {
|
||||
if (eagerGet) {
|
||||
cacheHit = true;
|
||||
} else {
|
||||
retVal = cache.get(key);
|
||||
cacheHit = cache.containsKey(key);
|
||||
}
|
||||
}
|
||||
if (!cacheHit) {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " NOT found in cache(s), invoking cached target method "
|
||||
+ method);
|
||||
}
|
||||
retVal = invocation.call();
|
||||
} else {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " found in cache, returning value " + retVal);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cacheHit) {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " NOT found in cache(s), invoking cached target method "
|
||||
+ method);
|
||||
}
|
||||
retVal = invocation.call();
|
||||
}
|
||||
else {
|
||||
if (log) {
|
||||
logger.trace("Key " + key + " found in cache, returning value " + retVal);
|
||||
}
|
||||
}
|
||||
|
||||
// update all caches (if needed)
|
||||
for (Cache cache : caches) {
|
||||
cache.putIfAbsent(key, retVal);
|
||||
}
|
||||
// update all caches
|
||||
for (Cache cache : caches) {
|
||||
cache.put(key, retVal);
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheDef instanceof CacheInvalidateDefinition) {
|
||||
CacheInvalidateDefinition invalidateDef = (CacheInvalidateDefinition) cacheDef;
|
||||
if (cacheOp instanceof CacheEvictOperation) {
|
||||
CacheEvictOperation evictOp = (CacheEvictOperation) cacheOp;
|
||||
retVal = invocation.call();
|
||||
|
||||
// for each cache
|
||||
|
|
@ -270,10 +221,10 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
for (Cache cache : caches) {
|
||||
// flush the cache (ignore arguments)
|
||||
if (invalidateDef.isCacheWide()) {
|
||||
if (evictOp.isCacheWide()) {
|
||||
cache.clear();
|
||||
if (log) {
|
||||
logger.trace("Invalidating entire cache for definition " + cacheDef + " on method "
|
||||
logger.trace("Invalidating entire cache for definition " + cacheOp + " on method "
|
||||
+ method);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -282,19 +233,18 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
key = context.generateKey();
|
||||
}
|
||||
if (log) {
|
||||
logger.trace("Invalidating cache key " + key + " for definition " + cacheDef
|
||||
logger.trace("Invalidating cache key " + key + " for definition " + cacheOp
|
||||
+ " on method " + method);
|
||||
}
|
||||
cache.remove(key);
|
||||
cache.evict(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (log) {
|
||||
logger.trace("Cache condition failed on method " + method + " for definition " + cacheDef);
|
||||
logger.trace("Cache condition failed on method " + method + " for definition " + cacheOp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -304,7 +254,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
protected class CacheOperationContext {
|
||||
|
||||
private CacheDefinition definition;
|
||||
private CacheOperation operation;
|
||||
private final Collection<Cache<?, ?>> caches;
|
||||
private final Object target;
|
||||
private final Method method;
|
||||
|
|
@ -315,10 +265,10 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
private final KeyGenerator<?> keyGenerator = CacheAspectSupport.this.keyGenerator;
|
||||
|
||||
public CacheOperationContext(CacheDefinition operationDefinition, Method method, Object[] args,
|
||||
Object target, Class<?> targetClass) {
|
||||
this.definition = operationDefinition;
|
||||
this.caches = CacheAspectSupport.this.getCaches(definition);
|
||||
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target,
|
||||
Class<?> targetClass) {
|
||||
this.operation = operation;
|
||||
this.caches = CacheAspectSupport.this.getCaches(operation);
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
|
|
@ -329,12 +279,12 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
/**
|
||||
* Evaluates the definition condition.
|
||||
*
|
||||
* @param definition
|
||||
* @param operation
|
||||
* @return
|
||||
*/
|
||||
protected boolean hasConditionPassed() {
|
||||
if (StringUtils.hasText(definition.getCondition())) {
|
||||
return evaluator.condition(definition.getCondition(), method, evalContext);
|
||||
if (StringUtils.hasText(operation.getCondition())) {
|
||||
return evaluator.condition(operation.getCondition(), method, evalContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -342,7 +292,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
/**
|
||||
* Computes the key for the given caching definition.
|
||||
*
|
||||
* @param definition
|
||||
* @param operation
|
||||
* @param method
|
||||
* method being invoked
|
||||
* @param objects
|
||||
|
|
@ -350,8 +300,8 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
* @return generated key (null if none can be generated)
|
||||
*/
|
||||
protected Object generateKey() {
|
||||
if (StringUtils.hasText(definition.getKey())) {
|
||||
return evaluator.key(definition.getKey(), method, evalContext);
|
||||
if (StringUtils.hasText(operation.getKey())) {
|
||||
return evaluator.key(operation.getKey(), method, evalContext);
|
||||
}
|
||||
|
||||
return keyGenerator.extract(target, method, args);
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Interface describing Spring-compliant caching operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface CacheDefinition {
|
||||
|
||||
/**
|
||||
* Returns the name of this operation. Can be <tt>null</tt>.
|
||||
* In case of Spring's declarative caching, the exposed name will be:
|
||||
* <tt>fully qualified class name.method name</tt>.
|
||||
*
|
||||
* @return the operation name
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Returns the names of the cache against which this operation is performed.
|
||||
*
|
||||
* @return names of the cache on which the operation is performed.
|
||||
*/
|
||||
Set<String> getCacheNames();
|
||||
|
||||
/**
|
||||
* Returns the SpEL expression conditioning the operation.
|
||||
*
|
||||
* @return operation condition (as SpEL expression).
|
||||
*/
|
||||
String getCondition();
|
||||
|
||||
/**
|
||||
* Returns the SpEL expression identifying the cache key.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getKey();
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
||||
/**
|
||||
* Interface used by CacheInterceptor. Implementations know
|
||||
* how to source cache operation attributes, whether from configuration,
|
||||
* metadata attributes at source level, or anywhere else.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface CacheDefinitionSource {
|
||||
|
||||
/**
|
||||
* Return the cache operation definition for this method.
|
||||
* Return null if the method is not cacheable.
|
||||
* @param method method
|
||||
* @param targetClass target class. May be <code>null</code>, in which
|
||||
* case the declaring class of the method must be used.
|
||||
* @return {@link CacheDefinition} the matching cache operation definition,
|
||||
* or <code>null</code> if none found
|
||||
*/
|
||||
CacheDefinition getCacheDefinition(Method method, Class<?> targetClass);
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Inner class that implements a Pointcut that matches if the underlying
|
||||
* {@link CacheDefinitionSource} has an attribute for a given method.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
abstract class CacheDefinitionSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
CacheDefinitionSource cas = getCacheDefinitionSource();
|
||||
return (cas == null || cas.getCacheDefinition(method, targetClass) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof CacheDefinitionSourcePointcut)) {
|
||||
return false;
|
||||
}
|
||||
CacheDefinitionSourcePointcut otherPc = (CacheDefinitionSourcePointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getCacheDefinitionSource(),
|
||||
otherPc.getCacheDefinitionSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return CacheDefinitionSourcePointcut.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + getCacheDefinitionSource();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the underlying CacheOperationDefinitionSource (may be <code>null</code>).
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
protected abstract CacheDefinitionSource getCacheDefinitionSource();
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
|
||||
/**
|
||||
* Interface describing a Spring cache invalidation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface CacheInvalidateDefinition extends CacheDefinition {
|
||||
|
||||
/**
|
||||
* Returns whether the operation affects the entire cache or not.
|
||||
*
|
||||
* @return whether the operation is cache wide or not.
|
||||
*/
|
||||
boolean isCacheWide();
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
|
|||
*
|
||||
* @param cacheDefinitionSources cache definition sources
|
||||
*/
|
||||
public void setCacheDefinitionSources(CacheDefinitionSource... cacheDefinitionSources) {
|
||||
public void setCacheDefinitionSources(CacheOperationSource... cacheDefinitionSources) {
|
||||
this.cachingInterceptor.setCacheDefinitionSources(cacheDefinitionSources);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
/**
|
||||
* Interface describing a Spring cache update.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public interface CacheUpdateDefinition extends CacheDefinition {
|
||||
|
||||
/**
|
||||
* Returns the SpEL expression identifying the cache key.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getKey();
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Composite {@link CacheDefinitionSource} implementation that iterates
|
||||
* over a given array of {@link CacheDefinitionSource} instances.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CompositeCacheDefinitionSource implements CacheDefinitionSource, Serializable {
|
||||
|
||||
private final CacheDefinitionSource[] cacheDefinitionSources;
|
||||
|
||||
/**
|
||||
* Create a new CompositeCachingDefinitionSource for the given sources.
|
||||
* @param cacheDefinitionSourcess the CacheDefinitionSource instances to combine
|
||||
*/
|
||||
public CompositeCacheDefinitionSource(CacheDefinitionSource[] cacheDefinitionSources) {
|
||||
Assert.notNull(cacheDefinitionSources, "cacheDefinitionSource array must not be null");
|
||||
this.cacheDefinitionSources = cacheDefinitionSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheDefinitionSource instances that this
|
||||
* CompositeCachingDefinitionSource combines.
|
||||
*/
|
||||
public final CacheDefinitionSource[] getCacheDefinitionSources() {
|
||||
return this.cacheDefinitionSources;
|
||||
}
|
||||
|
||||
|
||||
public CacheDefinition getCacheDefinition(Method method, Class<?> targetClass) {
|
||||
for (CacheDefinitionSource source : cacheDefinitionSources) {
|
||||
CacheDefinition definition = source.getCacheDefinition(method, targetClass);
|
||||
if (definition != null) {
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link CacheInvalidateDefinition} interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultCacheInvalidateDefinition extends AbstractCacheDefinition implements
|
||||
CacheInvalidateDefinition {
|
||||
|
||||
private boolean cacheWide = false;
|
||||
|
||||
public boolean isCacheWide() {
|
||||
return cacheWide;
|
||||
}
|
||||
|
||||
public void setCacheWide(boolean cacheWide) {
|
||||
this.cacheWide = cacheWide;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getDefinitionDescription() {
|
||||
StringBuilder sb = super.getDefinitionDescription();
|
||||
sb.append(",");
|
||||
sb.append(cacheWide);
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
/**
|
||||
* Default implementation of the {@link CacheUpdateDefinition} interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class DefaultCacheUpdateDefinition extends AbstractCacheDefinition implements CacheUpdateDefinition {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -20,14 +20,16 @@ import java.io.Serializable;
|
|||
import java.util.Map;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.DefaultValue;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class delegating most of the {@link Map}-like methods
|
||||
* to the underlying cache.
|
||||
*
|
||||
* <b>Note:</b>Allows null values to be stored, even if the underlying map
|
||||
* does not support them.
|
||||
* <b>Note:</b>Allows null values to be stored even (for cases where the
|
||||
* underlying cache does not support them) as long as arbitrary serialized
|
||||
* objects are supported.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
|
|
@ -57,7 +59,7 @@ public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
|
|||
*
|
||||
* @param <D> map type
|
||||
* @param delegate map delegate
|
||||
* @param allowNullValues flag indicating whether null values are allowed or not
|
||||
* @param allowNullValues flag indicating whether null values should be replaced or not
|
||||
*/
|
||||
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate, boolean allowNullValues) {
|
||||
Assert.notNull(delegate);
|
||||
|
|
@ -73,30 +75,22 @@ public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
|
|||
delegate.clear();
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return delegate.containsKey(key);
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
return filterNull(delegate.get(key));
|
||||
public ValueWrapper<V> get(Object key) {
|
||||
return new DefaultValue<V>(filterNull(delegate.get(key)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public V put(K key, V value) {
|
||||
public void put(K key, V value) {
|
||||
if (allowNullValues && value == null) {
|
||||
Map map = delegate;
|
||||
Object val = map.put(key, NULL_HOLDER);
|
||||
if (val == NULL_HOLDER) {
|
||||
return null;
|
||||
}
|
||||
return (V) val;
|
||||
map.put(key, NULL_HOLDER);
|
||||
}
|
||||
|
||||
return filterNull(delegate.put(key, value));
|
||||
delegate.put(key, value);
|
||||
}
|
||||
|
||||
public V remove(Object key) {
|
||||
return filterNull(delegate.remove(key));
|
||||
public void evict(Object key) {
|
||||
delegate.remove(key);
|
||||
}
|
||||
|
||||
protected V filterNull(V val) {
|
||||
|
|
|
|||
|
|
@ -54,11 +54,9 @@ public class EhCacheCacheTest extends AbstractNativeCacheTest<Ehcache> {
|
|||
brancusi.setTimeToLive(3);
|
||||
nativeCache.put(brancusi);
|
||||
|
||||
assertTrue(cache.containsKey(key));
|
||||
assertEquals(value, cache.get(key));
|
||||
assertEquals(value, cache.get(key).get());
|
||||
// wait for the entry to expire
|
||||
Thread.sleep(5 * 1000);
|
||||
assertFalse("expired entry returned", cache.containsKey(key));
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,6 @@ public abstract class AbstractNativeCacheTest<T> {
|
|||
|
||||
@Test
|
||||
public void testCachePut() throws Exception {
|
||||
|
||||
Object key = "enescu";
|
||||
Object value = "george";
|
||||
|
||||
|
|
@ -74,8 +73,6 @@ public abstract class AbstractNativeCacheTest<T> {
|
|||
|
||||
assertNull(cache.get(key));
|
||||
cache.put(key, value);
|
||||
assertEquals(value, cache.remove(key));
|
||||
assertNull(cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -88,66 +85,4 @@ public abstract class AbstractNativeCacheTest<T> {
|
|||
assertNull(cache.get("vlaicu"));
|
||||
assertNull(cache.get("enescu"));
|
||||
}
|
||||
|
||||
// concurrent map tests
|
||||
@Test
|
||||
public void testPutIfAbsent() throws Exception {
|
||||
Object key = "enescu";
|
||||
Object value1 = "george";
|
||||
Object value2 = "geo";
|
||||
|
||||
assertNull(cache.get("enescu"));
|
||||
cache.put(key, value1);
|
||||
cache.putIfAbsent(key, value2);
|
||||
assertEquals(value1, cache.get(key));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentRemove() throws Exception {
|
||||
Object key = "enescu";
|
||||
Object value1 = "george";
|
||||
Object value2 = "geo";
|
||||
|
||||
assertNull(cache.get("enescu"));
|
||||
cache.put(key, value1);
|
||||
// no remove
|
||||
cache.remove(key, value2);
|
||||
assertEquals(value1, cache.get(key));
|
||||
// one remove
|
||||
cache.remove(key, value1);
|
||||
assertNull(cache.get("enescu"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentReplace() throws Exception {
|
||||
Object key = "enescu";
|
||||
Object value1 = "george";
|
||||
Object value2 = "geo";
|
||||
|
||||
assertNull(cache.get("enescu"));
|
||||
cache.put(key, value1);
|
||||
cache.replace(key, value2);
|
||||
assertEquals(value2, cache.get(key));
|
||||
cache.remove(key);
|
||||
cache.replace(key, value1);
|
||||
assertNull(cache.get("enescu"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentReplaceIfEqual() throws Exception {
|
||||
Object key = "enescu";
|
||||
Object value1 = "george";
|
||||
Object value2 = "geo";
|
||||
|
||||
assertNull(cache.get("enescu"));
|
||||
cache.put(key, value1);
|
||||
assertEquals(value1, cache.get(key));
|
||||
// no replace
|
||||
cache.replace(key, value2, value1);
|
||||
assertEquals(value1, cache.get(key));
|
||||
cache.replace(key, value1, value2);
|
||||
assertEquals(value2, cache.get(key));
|
||||
cache.replace(key, value2, value1);
|
||||
assertEquals(value1, cache.get(key));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue