SPR-7308
SPR-7736 + clarify storage of null values
This commit is contained in:
@@ -18,7 +18,11 @@ package org.springframework.cache;
|
||||
|
||||
|
||||
/**
|
||||
* Interface that defines the common cache operations.
|
||||
* Interface that defines the common cache operations.
|
||||
*
|
||||
* <b>Note:</b> Due to the generic use of caching, it is recommended that
|
||||
* implementations allow storage of <tt>null</tt> values (for example to
|
||||
* cache methods that return null).
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
|
||||
@@ -22,9 +22,9 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.AbstractDelegatingCache;
|
||||
|
||||
/**
|
||||
* Simple {@link Cache} implementation based on the JDK 1.5+ java.util.concurrent package.
|
||||
* Useful for testing or simple caching scenarios.
|
||||
/**
|
||||
* Simple {@link Cache} implementation based on the JDK 1.5+
|
||||
* java.util.concurrent package. Useful for testing or simple caching scenarios.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@@ -42,7 +42,7 @@ public class ConcurrentCache<K, V> extends AbstractDelegatingCache<K, V> {
|
||||
}
|
||||
|
||||
public ConcurrentCache(ConcurrentMap<K, V> delegate, String name) {
|
||||
super(delegate);
|
||||
super(delegate, true);
|
||||
this.store = delegate;
|
||||
this.name = name;
|
||||
}
|
||||
@@ -55,19 +55,51 @@ public class ConcurrentCache<K, V> extends AbstractDelegatingCache<K, V> {
|
||||
return store;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public V putIfAbsent(K key, V value) {
|
||||
return store.putIfAbsent(key, 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) {
|
||||
return store.replace(key, value);
|
||||
if (getAllowNullValues()) {
|
||||
if (value == null) {
|
||||
ConcurrentMap raw = store;
|
||||
return filterNull((V) raw.replace(key, NULL_HOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
return filterNull(store.replace(key, value));
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -61,14 +60,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public abstract class CacheAspectSupport implements InitializingBean {
|
||||
|
||||
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();
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private CacheManager cacheManager;
|
||||
@@ -124,7 +115,6 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
||||
}
|
||||
|
||||
protected Collection<Cache<?, ?>> getCaches(CacheDefinition definition) {
|
||||
// TODO: add behaviour for the default cache
|
||||
Set<String> cacheNames = definition.getCacheNames();
|
||||
|
||||
Collection<Cache<?,?>> caches = new ArrayList<Cache<?,?>>(cacheNames.size());
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cache.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
@@ -25,15 +26,47 @@ 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.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
|
||||
|
||||
private final Map<K, V> delegate;
|
||||
private static class NullHolder implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
|
||||
public static final Object NULL_HOLDER = new NullHolder();
|
||||
|
||||
private final Map<K, V> delegate;
|
||||
private final boolean allowNullValues;
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given delegate.
|
||||
*
|
||||
* @param <D> map type
|
||||
* @param delegate map delegate
|
||||
*/
|
||||
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate) {
|
||||
this(delegate, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance using the given delegate.
|
||||
*
|
||||
* @param <D> map type
|
||||
* @param delegate map delegate
|
||||
* @param allowNullValues flag indicating whether null values are allowed or not
|
||||
*/
|
||||
public <D extends Map<K, V>> AbstractDelegatingCache(D delegate, boolean allowNullValues) {
|
||||
Assert.notNull(delegate);
|
||||
this.delegate = delegate;
|
||||
this.allowNullValues = allowNullValues;
|
||||
}
|
||||
|
||||
public boolean getAllowNullValues() {
|
||||
return allowNullValues;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
@@ -45,14 +78,31 @@ public abstract class AbstractDelegatingCache<K, V> implements Cache<K, V> {
|
||||
}
|
||||
|
||||
public V get(Object key) {
|
||||
return delegate.get(key);
|
||||
return filterNull(delegate.get(key));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public V put(K key, V value) {
|
||||
return delegate.put(key, value);
|
||||
if (allowNullValues && value == null) {
|
||||
Map map = delegate;
|
||||
Object val = map.put(key, NULL_HOLDER);
|
||||
if (val == NULL_HOLDER) {
|
||||
return null;
|
||||
}
|
||||
return (V) val;
|
||||
}
|
||||
|
||||
return filterNull(delegate.put(key, value));
|
||||
}
|
||||
|
||||
public V remove(Object key) {
|
||||
return delegate.remove(key);
|
||||
return filterNull(delegate.remove(key));
|
||||
}
|
||||
|
||||
protected V filterNull(V val) {
|
||||
if (allowNullValues && val == NULL_HOLDER) {
|
||||
return null;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user