Add missing cache-resolver attribute

Prior to this commit, CacheResolver could not be configured through
the XML namespace (i.e. cache:annotation-driven). This is now the
case.

Issue: SPR-11490
This commit is contained in:
Stephane Nicoll 2014-05-21 08:32:46 +02:00
parent 1338d46a6e
commit 9952973e01
10 changed files with 174 additions and 17 deletions

View File

@ -16,13 +16,15 @@
package org.springframework.cache.jcache.config;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
/**
@ -36,6 +38,16 @@ public class JCacheNamespaceDrivenTests extends AbstractJCacheAnnotationTests {
"/org/springframework/cache/jcache/config/jCacheNamespaceDriven.xml");
}
@Test
public void cacheResolver() {
ConfigurableApplicationContext context = new GenericXmlApplicationContext(
"/org/springframework/cache/jcache/config/jCacheNamespaceDriven-resolver.xml");
DefaultJCacheOperationSource ci = context.getBean(DefaultJCacheOperationSource.class);
assertSame(context.getBean("cacheResolver"), ci.getDefaultCacheResolver());
context.close();
}
@Test
public void testCacheErrorHandler() {
JCacheInterceptor ci = ctx.getBean(JCacheInterceptor.class);

View File

@ -0,0 +1,31 @@
<?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:cache="http://www.springframework.org/schema/cache"
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">
<cache:annotation-driven cache-manager="cacheManager" cache-resolver="cacheResolver"/>
<!-- We can't hid the cache manager completely as the exception cache resolver needs it -->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<ref bean="defaultCache"/>
</set>
</property>
</bean>
<bean id="defaultCache"
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
<property name="name" value="default"/>
</bean>
<bean id="cacheResolver" class="org.springframework.cache.interceptor.SimpleCacheResolver">
<property name="cacheManager" ref="cacheManager"/>
</bean>
</beans>

View File

@ -42,7 +42,7 @@ public interface CachingConfigurer {
/**
* Return the cache manager bean to use for annotation-driven cache
* management. A default {@link CacheResolver} will be initialized
* behind the scene with this cache manager. For more fine-grained
* behind the scenes with this cache manager. For more fine-grained
* management of the cache resolution, consider setting the
* {@link CacheResolver} directly.
* <p>Implementations must explicitly declare
@ -65,8 +65,10 @@ public interface CachingConfigurer {
/**
* Return the {@link CacheResolver} bean to use to resolve regular caches for
* annotation-driven cache management. This is an alternative option to set
* the {@link CacheManager} to use.
* annotation-driven cache management. This is an alternative and more powerful
* option of specifying the {@link CacheManager} to use.
* <p>If both a {@link #cacheManager()} and {@link #cacheResolver()} are set, the
* cache manager is ignored.
* <p>Implementations must explicitly declare
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
* <pre class="code">

View File

@ -98,9 +98,20 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
}
}
private static void parseCacheManagerProperty(Element element, BeanDefinition def) {
def.getPropertyValues().add("cacheManager",
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
/**
* Parse the cache resolution strategy to use. If a 'cache-resolver' attribute
* is set, it is injected. Otherwise the 'cache-manager' is set. If {@code setBoth}
* is {@code true}, both service are actually injected.
*/
private static void parseCacheResolution(Element element, BeanDefinition def, boolean setBoth) {
String name = element.getAttribute("cache-resolver");
if (StringUtils.hasText(name)) {
def.getPropertyValues().add("cacheResolver", new RuntimeBeanReference(name.trim()));
}
if (!StringUtils.hasText(name) || setBoth) {
def.getPropertyValues().add("cacheManager",
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
}
}
private static BeanDefinition parseErrorHandler(Element element, BeanDefinition def) {
@ -130,7 +141,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parseCacheManagerProperty(element, interceptorDef);
parseCacheResolution(element, interceptorDef, false);
parseErrorHandler(element, interceptorDef);
CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef);
interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
@ -170,7 +181,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
RootBeanDefinition def = new RootBeanDefinition();
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
def.setFactoryMethodName("aspectOf");
parseCacheManagerProperty(element, def);
parseCacheResolution(element, def, false);
CacheNamespaceHandler.parseKeyGenerator(element, def);
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
}
@ -239,7 +250,9 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
RootBeanDefinition sourceDef = new RootBeanDefinition(JCACHE_OPERATION_SOURCE_CLASS);
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
parseCacheManagerProperty(element, sourceDef);
// JSR-107 support should create an exception cache resolver with the cache manager
// and there is no way to set that exception cache resolver from the namespace
parseCacheResolution(element, sourceDef, true);
CacheNamespaceHandler.parseKeyGenerator(element, sourceDef);
return sourceDef;
}

View File

@ -32,6 +32,9 @@ import org.springframework.cache.CacheManager;
*/
public class SimpleCacheResolver extends BaseCacheResolver {
public SimpleCacheResolver() {
}
public SimpleCacheResolver(CacheManager cacheManager) {
super(cacheManager);
}

View File

@ -26,7 +26,10 @@
annotations on bean classes, and that proxies are automatically
to be created for the relevant annotated beans.
The default annotations supported are Spring's @Cacheable and @CacheEvict.
The default annotations supported are Spring's @Cacheable, @CachePut and @CacheEvict. If
spring-context-support and the JSR-107 API are on the classpath, additional proxies are
automatically created for JSR-107 annotated beans, that is @CacheResult, @CachePut,
@CacheRemove and @CacheRemoveAll.
See org.springframework.cache.annotation.EnableCaching Javadoc
for information on code-based alternatives to this XML element.
@ -35,11 +38,15 @@
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[
The bean name of the CacheManager that is to be used to retrieve the backing caches.
The bean name of the CacheManager that is to be used to retrieve the backing
caches. A default CacheResolver will be initialized behind the scenes with
this cache manager (or "cacheManager" if not set). For more fine-grained
management of the cache resolution, consider setting the 'cache-resolver'
attribute.
This attribute is not required, and only needs to be specified
explicitly if the bean name of the desired CacheManager
is not 'cacheManager'.
Note that this attribute is still mandatory if you are using JSR-107 as an
additional exception cache resolver should be created and requires a CacheManager
to do so.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
@ -48,6 +55,21 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="cache-resolver" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.cache.interceptor.CacheResolver"><![CDATA[
The bean name of the CacheResolver that is to be used to resolve the backing caches.
This attribute is not required, and only needs to be specified as an alternative to
the 'cache-manager' attribute. See the javadoc of CacheResolver for more details.
]]></xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.cache.interceptor.CacheResolver"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="key-generator" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.cache.interceptor.KeyGenerator"><![CDATA[

View File

@ -28,6 +28,7 @@ import org.springframework.context.support.GenericXmlApplicationContext;
/**
* @author Costin Leau
* @author Chris Beams
* @author Stephane Nicoll
*/
public class AnnotationNamespaceDrivenTests extends AbstractAnnotationTests {
@ -44,6 +45,26 @@ public class AnnotationNamespaceDrivenTests extends AbstractAnnotationTests {
assertSame(ctx.getBean("keyGenerator"), ci.getKeyGenerator());
}
@Test
public void cacheResolver() {
ConfigurableApplicationContext context = new GenericXmlApplicationContext(
"/org/springframework/cache/config/annotationDrivenCacheNamespace-resolver.xml");
CacheInterceptor ci = context.getBean(CacheInterceptor.class);
assertSame(context.getBean("cacheResolver"), ci.getCacheResolver());
context.close();
}
@Test
public void bothSetOnlyResolverIsUsed() {
ConfigurableApplicationContext context = new GenericXmlApplicationContext(
"/org/springframework/cache/config/annotationDrivenCacheNamespace-manager-resolver.xml");
CacheInterceptor ci = context.getBean(CacheInterceptor.class);
assertSame(context.getBean("cacheResolver"), ci.getCacheResolver());
context.close();
}
@Test
public void testCacheErrorHandler() {
CacheInterceptor ci = ctx.getBean("org.springframework.cache.interceptor.CacheInterceptor#0",

View File

@ -0,0 +1,22 @@
<?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: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/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven cache-manager="customCacheManager"
cache-resolver="cacheResolver"/>
<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="cacheResolver" class="org.springframework.cache.interceptor.SimpleCacheResolver">
<property name="cacheManager" ref="customCacheManager"/>
</bean>
</beans>

View File

@ -0,0 +1,22 @@
<?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: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/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven cache-resolver="cacheResolver"/>
<bean id="cacheResolver" class="org.springframework.cache.interceptor.SimpleCacheResolver">
<property name="cacheManager">
<bean class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
p:name="default"/>
</set>
</property>
</bean>
</property>
</bean>
</beans>

View File

@ -47193,8 +47193,17 @@ application through AOP. The configuration is intentionally similar with that of
| `cache-manager`
| N/A (See `CachingConfigurer` javadocs)
| cacheManager
| Name of cache manager to use. Only required if the name of the cache manager is not
`cacheManager`.
| Name of cache manager to use. A default `CacheResolver` will be initialized behind
the scenes with this cache manager (or `cacheManager`if not set). For more
fine-grained management of the cache resolution, consider setting the 'cache-resolver'
attribute.
| `cache-resolver`
| N/A (See `CachingConfigurer` javadocs)
| A `SimpleCacheResolver` using the configured `cacheManager`.
| The bean name of the CacheResolver that is to be used to resolve the backing caches.
This attribute is not required, and only needs to be specified as an alternative to
the 'cache-manager' attribute.
| `key-generator`
| N/A (See `CachingConfigurer` javadocs)