Add custom cache manager per cache operation

It is now possible to specify the CacheManager to use per operation.
The related cache annotation now has an extra attribute that defines
the name of the CacheManager bean to use.  The cache manager that
was previously used is therefore a 'default' cache manager (i.e. the
one to use if no custom cache manager has been set on the operation).

Issue: SPR-8696
This commit is contained in:
Stephane Nicoll
2014-02-21 14:24:00 +01:00
parent 81c208098f
commit f06cad91c0
21 changed files with 243 additions and 31 deletions

View File

@@ -117,6 +117,18 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "customCacheManager")
public Object customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "unknownBeanName")
public Object unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
public Object update(Object arg1) {

View File

@@ -62,6 +62,10 @@ public interface CacheableService<T> {
T unknownCustomKeyGenerator(Object arg1);
T customCacheManager(Object arg1);
T unknownCustomCacheManager(Object arg1);
T throwChecked(Object arg1) throws Exception;
T throwUnchecked(Object arg1);

View File

@@ -121,6 +121,18 @@ public class DefaultCacheableService implements CacheableService<Long> {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "customCacheManager")
public Long customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "unknownBeanName")
public Long unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
public Long update(Object arg1) {

View File

@@ -1,13 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<!--
@@ -45,6 +42,16 @@
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator" />
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
</bean>
</set>
</property>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>

View File

@@ -28,6 +28,7 @@ import java.lang.annotation.Target;
* a cache invalidate operation.
*
* @author Costin Leau
* @author Stephane Nicoll
* @since 3.1
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@@ -56,6 +57,11 @@ public @interface CacheEvict {
*/
String keyGenerator() default "";
/**
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
*/
String cacheManager() default "";
/**
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
* <p>Default is "", meaning the method is always cached.

View File

@@ -33,6 +33,7 @@ import org.springframework.cache.Cache;
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
* @since 3.1
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@@ -61,6 +62,11 @@ public @interface CachePut {
*/
String keyGenerator() default "";
/**
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
*/
String cacheManager() default "";
/**
* Spring Expression Language (SpEL) attribute used for conditioning the cache update.
* <p>Default is "", meaning the method result is always cached.

View File

@@ -31,6 +31,7 @@ import java.lang.annotation.Target;
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
* @since 3.1
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@@ -59,6 +60,11 @@ public @interface Cacheable {
*/
String keyGenerator() default "";
/**
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
*/
String cacheManager() default "";
/**
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
* <p>Default is "", meaning the method is always cached.

View File

@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @since 3.1
*/
@SuppressWarnings("serial")
@@ -88,6 +89,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
cuo.setUnless(caching.unless());
cuo.setKey(caching.key());
cuo.setKeyGenerator(caching.keyGenerator());
cuo.setCacheManager(caching.cacheManager());
cuo.setName(ae.toString());
checkKeySourceConsistency(ae, caching.key(), caching.keyGenerator());
@@ -100,6 +102,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
ceo.setCondition(caching.condition());
ceo.setKey(caching.key());
ceo.setKeyGenerator(caching.keyGenerator());
ceo.setCacheManager(caching.cacheManager());
ceo.setCacheWide(caching.allEntries());
ceo.setBeforeInvocation(caching.beforeInvocation());
ceo.setName(ae.toString());
@@ -115,6 +118,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
cuo.setUnless(caching.unless());
cuo.setKey(caching.key());
cuo.setKeyGenerator(caching.keyGenerator());
cuo.setCacheManager(caching.cacheManager());
cuo.setName(ae.toString());
checkKeySourceConsistency(ae, caching.key(), caching.keyGenerator());

View File

@@ -45,6 +45,7 @@ import org.w3c.dom.Element;
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
*/
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
@@ -186,6 +187,8 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
private String keyGenerator;
private String cacheManager;
private String condition;
private String method;
@@ -197,6 +200,7 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
String defaultCache = root.getAttribute("cache");
key = root.getAttribute("key");
keyGenerator = root.getAttribute("key-generator");
cacheManager = root.getAttribute("cache-manager");
condition = root.getAttribute("condition");
method = root.getAttribute(METHOD_ATTRIBUTE);
@@ -222,6 +226,7 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
op.setKey(getAttributeValue(element, "key", this.key));
op.setKeyGenerator(getAttributeValue(element, "key-generator", this.keyGenerator));
op.setCacheManager(getAttributeValue(element, "cache-manager", this.cacheManager));
op.setCondition(getAttributeValue(element, "condition", this.condition));
if (StringUtils.hasText(op.getKey()) && StringUtils.hasText(op.getKeyGenerator())) {

View File

@@ -88,14 +88,15 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
/**
* Set the CacheManager that this cache aspect should delegate to.
* Set the default {@link CacheManager} that this cache aspect should delegate to
* if no specific cache manager has been set for the operation.
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
/**
* Return the CacheManager that this cache aspect delegates to.
* Return the default {@link CacheManager} that this cache aspect delegates to.
*/
public CacheManager getCacheManager() {
return this.cacheManager;
@@ -164,14 +165,12 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
return ClassUtils.getQualifiedMethodName(specificMethod);
}
protected Collection<? extends Cache> getCaches(CacheOperation operation) {
protected Collection<? extends Cache> getCaches(CacheOperation operation, CacheManager cacheManager) {
Set<String> cacheNames = operation.getCacheNames();
Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache cache = this.cacheManager.getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named '" + cacheName + "' for " + operation);
}
Cache cache = cacheManager.getCache(cacheName);
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + operation);
caches.add(cache);
}
return caches;
@@ -386,6 +385,8 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
private final KeyGenerator operationKeyGenerator;
private final CacheManager operationCacheManager;
public CacheOperationContext(CacheOperation operation, Method method,
Object[] args, Object target, Class<?> targetClass) {
this.operation = operation;
@@ -393,13 +394,20 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
this.args = extractArgs(method, args);
this.target = target;
this.targetClass = targetClass;
this.caches = CacheAspectSupport.this.getCaches(operation);
if (StringUtils.hasText(operation.getKeyGenerator())) { // TODO: exception mgt?
this.operationKeyGenerator = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext, KeyGenerator.class, operation.getKeyGenerator());
} else {
this.operationKeyGenerator = keyGenerator;
}
if (StringUtils.hasText(operation.getCacheManager())) {
this.operationCacheManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
applicationContext, CacheManager.class, operation.getCacheManager());
}
else {
this.operationCacheManager = cacheManager;
}
this.caches = CacheAspectSupport.this.getCaches(operation, operationCacheManager);
}
private Object[] extractArgs(Method method, Object[] args) {

View File

@@ -26,6 +26,7 @@ import org.springframework.util.Assert;
* Base class for cache operations.
*
* @author Costin Leau
* @author Stephane Nicoll
*/
public abstract class CacheOperation {
@@ -37,6 +38,8 @@ public abstract class CacheOperation {
private String keyGenerator = "";
private String cacheManager = "";
private String name = "";
@@ -56,6 +59,10 @@ public abstract class CacheOperation {
return keyGenerator;
}
public String getCacheManager() {
return cacheManager;
}
public String getName() {
return name;
}
@@ -88,6 +95,11 @@ public abstract class CacheOperation {
this.keyGenerator = keyGenerator;
}
public void setCacheManager(String cacheManager) {
Assert.notNull(cacheManager);
this.cacheManager = cacheManager;
}
public void setName(String name) {
Assert.hasText(name);
this.name = name;
@@ -137,6 +149,8 @@ public abstract class CacheOperation {
result.append(this.key);
result.append("' | keyGenerator='");
result.append(this.keyGenerator);
result.append("' | cacheManager='");
result.append(this.cacheManager);
result.append("' | condition='");
result.append(this.condition);
result.append("'");

View File

@@ -185,6 +185,12 @@
The name of the KeyGenerator bean responsible to compute the key, mutually exclusive with the key parameter.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-manager" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[
The name of the CacheManager bean responsible to manage the operation.]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="condition" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -34,6 +34,7 @@ import org.springframework.util.ReflectionUtils;
/**
* @author Costin Leau
* @author Stephane Nicoll
*/
public class AnnotationCacheOperationSourceTests {
@@ -116,6 +117,22 @@ public class AnnotationCacheOperationSourceTests {
}
}
@Test
public void testCustomCacheManager() {
Collection<CacheOperation> ops = getOps("customCacheManager");
assertEquals(1, ops.size());
CacheOperation cacheOperation = ops.iterator().next();
assertEquals("Custom cache manager not set", "custom", cacheOperation.getCacheManager());
}
@Test
public void testCustomCacheManagerInherited() {
Collection<CacheOperation> ops = getOps("customCacheManagerInherited");
assertEquals(1, ops.size());
CacheOperation cacheOperation = ops.iterator().next();
assertEquals("Custom cache manager not set", "custom", cacheOperation.getCacheManager());
}
private static class AnnotatedClass {
@Cacheable("test")
public void singular() {
@@ -134,6 +151,10 @@ public class AnnotationCacheOperationSourceTests {
public void customKeyGenerator() {
}
@Cacheable(value = "test", cacheManager = "custom")
public void customCacheManager() {
}
@EvictFoo
public void singleStereotype() {
}
@@ -155,6 +176,10 @@ public class AnnotationCacheOperationSourceTests {
@Cacheable(value = "test", key = "#root.methodName", keyGenerator = "custom")
public void invalidKeyAndKeyGeneratorSet() {
}
@CacheableFooCustomCacheManager
public void customCacheManagerInherited() {
}
}
@Retention(RetentionPolicy.RUNTIME)
@@ -169,6 +194,13 @@ public class AnnotationCacheOperationSourceTests {
public @interface CacheableFooCustomKeyGenerator {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Cacheable(value = "foo", cacheManager = "custom")
public @interface CacheableFooCustomCacheManager {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@CacheEvict(value = "foo")

View File

@@ -36,6 +36,7 @@ import org.springframework.context.ApplicationContext;
* @author Costin Leau
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
*/
public abstract class AbstractAnnotationTests {
@@ -47,6 +48,8 @@ public abstract class AbstractAnnotationTests {
protected CacheManager cm;
protected CacheManager customCm;
/** @return a refreshed application context */
protected abstract ApplicationContext getApplicationContext();
@@ -55,7 +58,9 @@ public abstract class AbstractAnnotationTests {
ctx = getApplicationContext();
cs = ctx.getBean("service", CacheableService.class);
ccs = ctx.getBean("classService", CacheableService.class);
cm = ctx.getBean(CacheManager.class);
cm = ctx.getBean("cacheManager", CacheManager.class);
customCm = ctx.getBean("customCacheManager", CacheManager.class);
Collection<String> cn = cm.getCacheNames();
assertTrue(cn.contains("default"));
assertTrue(cn.contains("secondary"));
@@ -591,6 +596,26 @@ public abstract class AbstractAnnotationTests {
}
}
@Test
public void testCustomCacheManager() {
Object key = new Object();
Object r1 = cs.customCacheManager(key);
assertSame(r1, cs.customCacheManager(key));
Cache cache = customCm.getCache("default");
assertNotNull(cache.get(key));
}
@Test
public void testUnknownCustomCacheManager() {
try {
Object param = new Object();
cs.unknownCustomCacheManager(param);
fail("should have failed with NoSuchBeanDefinitionException");
} catch (NoSuchBeanDefinitionException e) {
// expected
}
}
@Test
public void testNullArg() throws Exception {
testNullArg(cs);

View File

@@ -26,6 +26,7 @@ import org.springframework.cache.annotation.Caching;
/**
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
*/
@Cacheable("default")
public class AnnotatedClassCacheableService implements CacheableService<Object> {
@@ -119,6 +120,18 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "customCacheManager")
public Object customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "unknownBeanName")
public Object unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
public Object update(Object arg1) {

View File

@@ -21,6 +21,7 @@ package org.springframework.cache.config;
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
*/
public interface CacheableService<T> {
@@ -62,6 +63,10 @@ public interface CacheableService<T> {
T unknownCustomKeyGenerator(Object arg1);
T customCacheManager(Object arg1);
T unknownCustomCacheManager(Object arg1);
T throwChecked(Object arg1) throws Exception;
T throwUnchecked(Object arg1);

View File

@@ -28,6 +28,7 @@ import org.springframework.cache.annotation.Caching;
*
* @author Costin Leau
* @author Phillip Webb
* @author Stephane Nicoll
*/
public class DefaultCacheableService implements CacheableService<Long> {
@@ -121,6 +122,18 @@ public class DefaultCacheableService implements CacheableService<Long> {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "customCacheManager")
public Long customCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@Cacheable(value = "default", cacheManager = "unknownBeanName")
public Long unknownCustomCacheManager(Object arg1) {
return counter.getAndIncrement();
}
@Override
@CachePut("default")
public Long update(Object arg1) {

View File

@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Configuration;
* Integration tests for @EnableCaching and its related @Configuration classes.
*
* @author Chris Beams
* @author Stephane Nicoll
*/
public class EnableCachingTests extends AbstractAnnotationTests {
@@ -145,6 +146,13 @@ public class EnableCachingTests extends AbstractAnnotationTests {
public KeyGenerator customKeyGenerator() {
return new SomeCustomKeyGenerator();
}
@Bean
public CacheManager customCacheManager() {
SimpleCacheManager cm = new SimpleCacheManager();
cm.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
return cm;
}
}

View File

@@ -2,27 +2,27 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="apc" class="org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator"/>
<bean id="annotationSource" class="org.springframework.cache.annotation.AnnotationCacheOperationSource"/>
<aop:config>
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
</aop:config>
<bean id="cacheInterceptor" class="org.springframework.cache.interceptor.CacheInterceptor">
<property name="cacheManager" ref="cacheManager"/>
<property name="cacheOperationSources" ref="annotationSource"/>
</bean>
<bean id="advisor" class="org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor">
<property name="cacheOperationSource" ref="annotationSource"/>
<property name="adviceBeanName" value="cacheInterceptor"/>
</bean>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
@@ -35,13 +35,21 @@
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
<bean id="keyGenerator" class="org.springframework.cache.config.SomeKeyGenerator"/>
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator"/>
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set>
</property>
</bean>
</beans>

View File

@@ -3,16 +3,16 @@
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven proxy-target-class="false" order="0" key-generator="keyGenerator"/>
<aop:config>
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
</aop:config>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
@@ -24,13 +24,21 @@
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
<bean id="keyGenerator" class="org.springframework.cache.config.SomeKeyGenerator"/>
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator"/>
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set>
</property>
</bean>
</beans>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
@@ -18,6 +18,8 @@
<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
<cache:cacheable method="customKeyGenerator" key-generator="customKeyGenerator"/>
<cache:cacheable method="unknownCustomKeyGenerator" key-generator="unknownBeanName"/>
<cache:cacheable method="customCacheManager" cache-manager="customCacheManager"/>
<cache:cacheable method="unknownCustomCacheManager" cache-manager="unknownBeanName"/>
<cache:cacheable method="nullValue" cache="default"/>
</cache:caching>
<cache:caching>
@@ -111,6 +113,14 @@
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator"/>
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
</set>
</property>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>