revised cache abstraction

- removed generics from Cache/CacheManager (they add no value since it's an SPI not API)
+ update docs and tests
+ renamed ConcurrentCacheFactoryBean to ConcurrentMapCacheFactoryBean
This commit is contained in:
Costin Leau 2011-05-18 18:34:41 +00:00
parent dadd0f57ee
commit b39673aa79
19 changed files with 63 additions and 67 deletions

View File

@ -19,7 +19,7 @@
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <property name="caches">
<set> <set>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set> </set>
</property> </property>
</bean> </bean>

View File

@ -26,18 +26,18 @@ package org.springframework.cache;
* *
* @author Costin Leau * @author Costin Leau
*/ */
public interface Cache<K, V> { public interface Cache {
/** /**
* A (wrapper) object representing a cache value. * A (wrapper) object representing a cache value.
*/ */
interface ValueWrapper<V> { interface ValueWrapper {
/** /**
* Returns the actual value in the cache. * Returns the actual value in the cache.
* *
* @return cache value * @return cache value
*/ */
V get(); Object get();
} }
/** /**
@ -62,7 +62,7 @@ public interface Cache<K, V> {
* @return the value to which this cache maps the specified key, or * @return the value to which this cache maps the specified key, or
* <tt>null</tt> if the cache contains no mapping for this key. * <tt>null</tt> if the cache contains no mapping for this key.
*/ */
ValueWrapper<V> get(Object key); ValueWrapper get(Object key);
/** /**
* Associates the specified value with the specified key in this cache. * Associates the specified value with the specified key in this cache.
@ -72,7 +72,7 @@ public interface Cache<K, V> {
* @param key key with which the specified value is to be associated. * @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key. * @param value value to be associated with the specified key.
*/ */
void put(K key, V value); void put(Object key, Object value);
/** /**
* Evicts the mapping for this key from this cache if it is present. * Evicts the mapping for this key from this cache if it is present.

View File

@ -32,7 +32,7 @@ public interface CacheManager {
* @param name cache identifier - cannot be null * @param name cache identifier - cannot be null
* @return associated cache or null if none is found * @return associated cache or null if none is found
*/ */
<K, V> Cache<K, V> getCache(String name); Cache getCache(String name);
/** /**
* Returns a collection of the caches known by this cache manager. * Returns a collection of the caches known by this cache manager.

View File

@ -34,14 +34,14 @@ import org.springframework.cache.interceptor.DefaultValueWrapper;
* *
* @author Costin Leau * @author Costin Leau
*/ */
public class ConcurrentMapCache<K, V> implements Cache<K, V> { public class ConcurrentMapCache implements Cache {
private static class NullHolder implements Serializable { private static class NullHolder implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }
private static final Object NULL_HOLDER = new NullHolder(); private static final Object NULL_HOLDER = new NullHolder();
private final ConcurrentMap<K, V> store; private final ConcurrentMap store;
private final String name; private final String name;
private final boolean allowNullValues; private final boolean allowNullValues;
@ -50,10 +50,10 @@ public class ConcurrentMapCache<K, V> implements Cache<K, V> {
} }
public ConcurrentMapCache(String name) { public ConcurrentMapCache(String name) {
this(new ConcurrentHashMap<K, V>(), name, true); this(new ConcurrentHashMap(), name, true);
} }
public ConcurrentMapCache(ConcurrentMap<K, V> delegate, String name, boolean allowNullValues) { public ConcurrentMapCache(ConcurrentMap delegate, String name, boolean allowNullValues) {
this.store = delegate; this.store = delegate;
this.name = name; this.name = name;
this.allowNullValues = allowNullValues; this.allowNullValues = allowNullValues;
@ -67,7 +67,7 @@ public class ConcurrentMapCache<K, V> implements Cache<K, V> {
return allowNullValues; return allowNullValues;
} }
public ConcurrentMap<K, V> getNativeCache() { public ConcurrentMap getNativeCache() {
return store; return store;
} }
@ -75,13 +75,12 @@ public class ConcurrentMapCache<K, V> implements Cache<K, V> {
store.clear(); store.clear();
} }
public ValueWrapper<V> get(Object key) { public ValueWrapper get(Object key) {
V v = store.get(key); Object v = store.get(key);
return (v != null ? new DefaultValueWrapper<V>(filterNull(v)) : null); return (v != null ? new DefaultValueWrapper(filterNull(v)) : null);
} }
@SuppressWarnings("unchecked") public void put(Object key, Object value) {
public void put(K key, V value) {
if (allowNullValues && value == null) { if (allowNullValues && value == null) {
Map map = store; Map map = store;
map.put(key, NULL_HOLDER); map.put(key, NULL_HOLDER);
@ -94,7 +93,7 @@ public class ConcurrentMapCache<K, V> implements Cache<K, V> {
store.remove(key); store.remove(key);
} }
protected V filterNull(V val) { protected Object filterNull(Object val) {
if (allowNullValues && val == NULL_HOLDER) { if (allowNullValues && val == NULL_HOLDER) {
return null; return null;
} }

View File

@ -28,19 +28,19 @@ import org.springframework.util.StringUtils;
* *
* @author Costin Leau * @author Costin Leau
*/ */
public class ConcurrentCacheFactoryBean<K, V> implements FactoryBean<ConcurrentMapCache<K, V>>, BeanNameAware, public class ConcurrentMapCacheFactoryBean implements FactoryBean<ConcurrentMapCache>, BeanNameAware,
InitializingBean { InitializingBean {
private String name = ""; private String name = "";
private ConcurrentMapCache<K, V> cache; private ConcurrentMapCache cache;
private ConcurrentMap<K, V> store; private ConcurrentMap store;
public void afterPropertiesSet() { public void afterPropertiesSet() {
cache = (store == null ? new ConcurrentMapCache<K, V>(name) : new ConcurrentMapCache<K, V>(store, name, true)); cache = (store == null ? new ConcurrentMapCache(name) : new ConcurrentMapCache(store, name, true));
} }
public ConcurrentMapCache<K, V> getObject() throws Exception { public ConcurrentMapCache getObject() throws Exception {
return cache; return cache;
} }
@ -62,7 +62,7 @@ public class ConcurrentCacheFactoryBean<K, V> implements FactoryBean<ConcurrentM
this.name = name; this.name = name;
} }
public void setStore(ConcurrentMap<K, V> store) { public void setStore(ConcurrentMap store) {
this.store = store; this.store = store;
} }
} }

View File

@ -29,7 +29,7 @@ import org.springframework.util.Assert;
* *
* @author Costin Leau * @author Costin Leau
*/ */
public class EhCacheCache implements Cache<Object, Object> { public class EhCacheCache implements Cache {
private final Ehcache cache; private final Ehcache cache;
@ -58,7 +58,7 @@ public class EhCacheCache implements Cache<Object, Object> {
cache.removeAll(); cache.removeAll();
} }
public ValueWrapper<Object> get(Object key) { public ValueWrapper get(Object key) {
Element element = cache.get(key); Element element = cache.get(key);
return (element != null ? new DefaultValueWrapper<Object>(element.getObjectValue()) : null); return (element != null ? new DefaultValueWrapper<Object>(element.getObjectValue()) : null);
} }

View File

@ -36,7 +36,7 @@ public class EhCacheCacheManager extends AbstractCacheManager {
private net.sf.ehcache.CacheManager cacheManager; private net.sf.ehcache.CacheManager cacheManager;
@Override @Override
protected Collection<Cache<?, ?>> loadCaches() { protected Collection<Cache> loadCaches() {
Assert.notNull(cacheManager, "a backing Ehcache cache manager is required"); Assert.notNull(cacheManager, "a backing Ehcache cache manager is required");
Status status = cacheManager.getStatus(); Status status = cacheManager.getStatus();
@ -44,7 +44,7 @@ public class EhCacheCacheManager extends AbstractCacheManager {
"an 'alive' Ehcache cache manager is required - current cache is " + status.toString()); "an 'alive' Ehcache cache manager is required - current cache is " + status.toString());
String[] names = cacheManager.getCacheNames(); String[] names = cacheManager.getCacheNames();
Collection<Cache<?, ?>> caches = new LinkedHashSet<Cache<?, ?>>(names.length); Collection<Cache> caches = new LinkedHashSet<Cache>(names.length);
for (String name : names) { for (String name : names) {
caches.add(new EhCacheCache(cacheManager.getEhcache(name))); caches.add(new EhCacheCache(cacheManager.getEhcache(name)));
@ -53,8 +53,7 @@ public class EhCacheCacheManager extends AbstractCacheManager {
return caches; return caches;
} }
@SuppressWarnings("unchecked") public Cache getCache(String name) {
public <K, V> Cache<K, V> getCache(String name) {
Cache cache = super.getCache(name); Cache cache = super.getCache(name);
if (cache == null) { if (cache == null) {
// check the Ehcache cache again // check the Ehcache cache again

View File

@ -124,13 +124,13 @@ public abstract class CacheAspectSupport implements InitializingBean {
cacheDefinitionSources) : cacheDefinitionSources[0]); cacheDefinitionSources) : cacheDefinitionSources[0]);
} }
protected Collection<Cache<?, ?>> getCaches(CacheOperation operation) { protected Collection<Cache> getCaches(CacheOperation operation) {
Set<String> cacheNames = operation.getCacheNames(); Set<String> cacheNames = operation.getCacheNames();
Collection<Cache<?, ?>> caches = new ArrayList<Cache<?, ?>>(cacheNames.size()); Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
for (String cacheName : cacheNames) { for (String cacheName : cacheNames) {
Cache<Object, Object> cache = cacheManager.getCache(cacheName); Cache cache = cacheManager.getCache(cacheName);
if (cache == null) { if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named [" + cacheName + "] for " + operation); throw new IllegalArgumentException("Cannot find cache named [" + cacheName + "] for " + operation);
} }
@ -145,7 +145,6 @@ public abstract class CacheAspectSupport implements InitializingBean {
return new CacheOperationContext(operation, method, args, target, targetClass); return new CacheOperationContext(operation, method, args, target, targetClass);
} }
@SuppressWarnings("unchecked")
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);
@ -163,7 +162,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
// analyze caching information // analyze caching information
if (cacheOp != null) { if (cacheOp != null) {
CacheOperationContext context = getOperationContext(cacheOp, method, args, target, targetClass); CacheOperationContext context = getOperationContext(cacheOp, method, args, target, targetClass);
Collection<Cache<?, ?>> caches = context.getCaches(); Collection<Cache> caches = context.getCaches();
if (context.hasConditionPassed()) { if (context.hasConditionPassed()) {
// check operation // check operation
@ -183,9 +182,9 @@ public abstract class CacheAspectSupport implements InitializingBean {
// for each cache // for each cache
boolean cacheHit = false; boolean cacheHit = false;
for (Iterator<Cache<?, ?>> iterator = caches.iterator(); iterator.hasNext() && !cacheHit;) { for (Iterator<Cache> iterator = caches.iterator(); iterator.hasNext() && !cacheHit;) {
Cache cache = iterator.next(); Cache cache = iterator.next();
Cache.ValueWrapper<Object> wrapper = cache.get(key); Cache.ValueWrapper wrapper = cache.get(key);
if (wrapper != null) { if (wrapper != null) {
cacheHit = true; cacheHit = true;
@ -255,7 +254,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
protected class CacheOperationContext { protected class CacheOperationContext {
private CacheOperation operation; private CacheOperation operation;
private final Collection<Cache<?, ?>> caches; private final Collection<Cache> caches;
private final Object target; private final Object target;
private final Method method; private final Method method;
private final Object[] args; private final Object[] args;
@ -307,7 +306,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
return keyGenerator.extract(target, method, args); return keyGenerator.extract(target, method, args);
} }
protected Collection<Cache<?, ?>> getCaches() { protected Collection<Cache> getCaches() {
return caches; return caches;
} }
} }

View File

@ -68,5 +68,5 @@ interface CacheExpressionRootObject {
* *
* @return current cache * @return current cache
*/ */
Collection<Cache<?,?>> getCaches(); Collection<Cache> getCaches();
} }

View File

@ -33,10 +33,10 @@ public class DefaultCacheExpressionRootObject implements CacheExpressionRootObje
private final Class<?> targetClass; private final Class<?> targetClass;
private final String methodName; private final String methodName;
private final Method method; private final Method method;
private final Collection<Cache<?, ?>> caches; private final Collection<Cache> caches;
private final Object[] args; private final Object[] args;
public DefaultCacheExpressionRootObject(Collection<Cache<?, ?>> caches, Method method, Object[] args, public DefaultCacheExpressionRootObject(Collection<Cache> caches, Method method, Object[] args,
Object target, Class<?> targetClass) { Object target, Class<?> targetClass) {
Assert.notNull(method, "method is required"); Assert.notNull(method, "method is required");
Assert.notNull(targetClass, "targetClass is required"); Assert.notNull(targetClass, "targetClass is required");
@ -52,7 +52,7 @@ public class DefaultCacheExpressionRootObject implements CacheExpressionRootObje
return methodName; return methodName;
} }
public Collection<Cache<?, ?>> getCaches() { public Collection<Cache> getCaches() {
return caches; return caches;
} }

View File

@ -23,15 +23,15 @@ import org.springframework.cache.Cache.ValueWrapper;
* *
* @author Costin Leau * @author Costin Leau
*/ */
public class DefaultValueWrapper<V> implements ValueWrapper<V> { public class DefaultValueWrapper<V> implements ValueWrapper {
private final V value; private final Object value;
public DefaultValueWrapper(V value) { public DefaultValueWrapper(Object value) {
this.value = value; this.value = value;
} }
public V get() { public Object get() {
return value; return value;
} }
} }

View File

@ -47,7 +47,7 @@ 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(Collection<Cache<?, ?>> caches, Method method, Object[] args, EvaluationContext createEvaluationContext(Collection<Cache> caches, Method method, Object[] args,
Object target, Class<?> targetClass) { Object target, Class<?> targetClass) {
DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(caches, method, args, DefaultCacheExpressionRootObject rootObject = new DefaultCacheExpressionRootObject(caches, method, args,
target, targetClass); target, targetClass);

View File

@ -37,11 +37,11 @@ import org.springframework.util.Assert;
public abstract class AbstractCacheManager implements CacheManager, InitializingBean { public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
// fast lookup by name map // fast lookup by name map
private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<String, Cache<?, ?>>(); private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
private Collection<String> names; private Collection<String> names;
public void afterPropertiesSet() { public void afterPropertiesSet() {
Collection<Cache<?, ?>> cacheSet = loadCaches(); Collection<Cache> cacheSet = loadCaches();
Assert.notEmpty(cacheSet); Assert.notEmpty(cacheSet);
@ -50,7 +50,7 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
// preserve the initial order of the cache names // preserve the initial order of the cache names
Set<String> cacheNames = new LinkedHashSet<String>(cacheSet.size()); Set<String> cacheNames = new LinkedHashSet<String>(cacheSet.size());
for (Cache<?, ?> cache : cacheSet) { for (Cache cache : cacheSet) {
caches.put(cache.getName(), cache); caches.put(cache.getName(), cache);
cacheNames.add(cache.getName()); cacheNames.add(cache.getName());
} }
@ -64,20 +64,19 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
* *
* @param caches the collection of caches handled by the manager * @param caches the collection of caches handled by the manager
*/ */
protected abstract Collection<Cache<?, ?>> loadCaches(); protected abstract Collection<Cache> loadCaches();
/** /**
* Returns the internal cache map. * Returns the internal cache map.
* *
* @return internal cache map * @return internal cache map
*/ */
protected final ConcurrentMap<String, Cache<?, ?>> getCacheMap() { protected final ConcurrentMap<String, Cache> getCacheMap() {
return caches; return caches;
} }
@SuppressWarnings("unchecked") public Cache getCache(String name) {
public <K, V> Cache<K, V> getCache(String name) { return caches.get(name);
return (Cache<K, V>) caches.get(name);
} }
public Collection<String> getCacheNames() { public Collection<String> getCacheNames() {

View File

@ -35,8 +35,8 @@ public class CompositeCacheManager implements CacheManager {
private CacheManager[] cacheManagers; private CacheManager[] cacheManagers;
public <K, V> Cache<K, V> getCache(String name) { public Cache getCache(String name) {
Cache<K, V> cache = null; Cache cache = null;
for (CacheManager cacheManager : cacheManagers) { for (CacheManager cacheManager : cacheManagers) {
cache = cacheManager.getCache(name); cache = cacheManager.getCache(name);
if (cache != null) { if (cache != null) {

View File

@ -28,14 +28,14 @@ import org.springframework.cache.Cache;
*/ */
public class SimpleCacheManager extends AbstractCacheManager { public class SimpleCacheManager extends AbstractCacheManager {
private Collection<Cache<?, ?>> caches; private Collection<Cache> caches;
@Override @Override
protected Collection<Cache<?, ?>> loadCaches() { protected Collection<Cache> loadCaches() {
return caches; return caches;
} }
public void setCaches(Collection<Cache<?, ?>> caches) { public void setCaches(Collection<Cache> caches) {
this.caches = caches; this.caches = caches;
} }
} }

View File

@ -133,7 +133,7 @@ public abstract class AbstractAnnotationTest {
Object key = new Object(); Object key = new Object();
Object r1 = service.name(key); Object r1 = service.name(key);
assertSame(r1, service.name(key)); assertSame(r1, service.name(key));
Cache<Object, Object> cache = cm.getCache("default"); Cache cache = cm.getCache("default");
// assert the method name is used // assert the method name is used
assertNotNull(cache.get(keyName)); assertNotNull(cache.get(keyName));
} }
@ -142,7 +142,7 @@ public abstract class AbstractAnnotationTest {
Object key = new Object(); Object key = new Object();
Object r1 = service.rootVars(key); Object r1 = service.rootVars(key);
assertSame(r1, service.rootVars(key)); assertSame(r1, service.rootVars(key));
Cache<Object, Object> cache = cm.getCache("default"); Cache cache = cm.getCache("default");
// assert the method name is used // assert the method name is used
String expectedKey = "rootVarsrootVars" + AopProxyUtils.ultimateTargetClass(service) + service; String expectedKey = "rootVarsrootVars" + AopProxyUtils.ultimateTargetClass(service) + service;
assertNotNull(cache.get(expectedKey)); assertNotNull(cache.get(expectedKey));

View File

@ -29,7 +29,7 @@
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <property name="caches">
<set> <set>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set> </set>
</property> </property>
</bean> </bean>

View File

@ -16,7 +16,7 @@
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <property name="caches">
<set> <set>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set> </set>
</property> </property>
</bean> </bean>

View File

@ -400,8 +400,8 @@ public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)]]><
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches"> <property name="caches">
<set> <set>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
<bean class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean" p:name="books"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/>
</set> </set>
</property> </property>
</bean>]]></programlisting> </bean>]]></programlisting>