Introduce @EnableCaching

See EnableCaching Javadoc for details.

Issue: SPR-8312
This commit is contained in:
Chris Beams 2011-11-16 04:21:21 +00:00
parent 732bf58570
commit 4f3cbb45f4
8 changed files with 852 additions and 0 deletions

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-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.aspectj;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.annotation.AbstractCachingConfiguration;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
/**
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
* to enable AspectJ-based annotation-driven cache management.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.cache.annotation.EnableCaching
* @see org.springframework.cache.annotation.CachingConfigurationSelector
*/
@Configuration
public class AspectJCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name=AnnotationConfigUtils.CACHE_ASPECT_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AnnotationCacheAspect cacheAspect() {
AnnotationCacheAspect cacheAspect = AnnotationCacheAspect.aspectOf();
if (this.cacheManager != null) {
cacheAspect.setCacheManager(this.cacheManager);
}
if (this.keyGenerator != null) {
cacheAspect.setKeyGenerator(this.keyGenerator);
}
return cacheAspect;
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2002-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.util.Collection;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Abstract base {@code @Configuration} class providing common structure for enabling
* Spring's annotation-driven cache management capability.
*
* @author Chris Beams
* @since 3.1
* @see EnableCaching
*/
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {
/** Parsed annotation metadata for {@code @EnableCaching} on the importing class. */
protected Map<String, Object> enableCaching;
protected CacheManager cacheManager;
protected KeyGenerator keyGenerator;
@Autowired(required=false)
private Collection<CacheManager> cacheManagerBeans;
@Autowired(required=false)
private Collection<CachingConfigurer> cachingConfigurers;
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = importMetadata.getAnnotationAttributes(
EnableCaching.class.getName(), false);
Assert.notNull(this.enableCaching,
"@EnableCaching is not present on importing class " +
importMetadata.getClassName());
}
/**
* Determine which {@code CacheManager} bean to use. Prefer the result of
* {@link CachingConfigurer#cacheManager()} over any by-type matching. If none, fall
* back to by-type matching on {@code CacheManager}.
* @throws IllegalArgumentException if no CacheManager can be found; if more than one
* CachingConfigurer implementation exists; if multiple CacheManager beans and no
* CachingConfigurer exists to disambiguate.
*/
@PostConstruct
protected void reconcileCacheManager() {
if (!CollectionUtils.isEmpty(cachingConfigurers)) {
int nConfigurers = cachingConfigurers.size();
if (nConfigurers > 1) {
throw new IllegalStateException(nConfigurers + " implementations of " +
"CachingConfigurer were found when only 1 was expected. " +
"Refactor the configuration such that CachingConfigurer is " +
"implemented only once or not at all.");
}
CachingConfigurer cachingConfigurer = cachingConfigurers.iterator().next();
this.cacheManager = cachingConfigurer.cacheManager();
this.keyGenerator = cachingConfigurer.keyGenerator();
}
else if (!CollectionUtils.isEmpty(cacheManagerBeans)) {
int nManagers = cacheManagerBeans.size();
if (nManagers > 1) {
throw new IllegalStateException(nManagers + " beans of type CacheManager " +
"were found when only 1 was expected. Remove all but one of the " +
"CacheManager bean definitions, or implement CachingConfigurer " +
"to make explicit which CacheManager should be used for " +
"annotation-driven cache management.");
}
CacheManager cacheManager = cacheManagerBeans.iterator().next();
this.cacheManager = cacheManager;
// keyGenerator remains null; will fall back to default within CacheInterceptor
}
else {
throw new IllegalStateException("No bean of type CacheManager could be found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation " +
"from your configuration.");
}
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright 2002-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.util.Map;
import org.springframework.aop.config.AopConfigUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportSelectorContext;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
/**
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
* based on the value of {@link EnableCaching#mode} on the importing @{@link Configuration}
* class.
*
* @author Chris Beams
* @since 3.1
* @see EnableCaching
* @see AbstractCachingConfiguration
* @see ProxyCachingConfiguration
* @see org.springframework.cache.aspectj.AspectJCachingConfiguration
*/
public class CachingConfigurationSelector implements ImportSelector {
/**
* {@inheritDoc}
* <p>This implementation selects {@link ProxyCachingConfiguration} if
* {@link EnableCaching#mode()} equals {@code PROXY}, and otherwise selects
* {@link org.springframework.cache.aspectj.AspectJCachingConfiguration AspectJCacheConfiguration}.
* <p>If {@code #mode()} equals {@code PROXY}, an auto-proxy creator bean definition
* will also be added to the enclosing {@link BeanDefinitionRegistry} and escalated
* if necessary through the usual {@link AopConfigUtils} family of methods.
*/
public String[] selectImports(ImportSelectorContext context) {
AnnotationMetadata importingClassMetadata = context.getImportingClassMetadata();
BeanDefinitionRegistry registry = context.getBeanDefinitionRegistry();
Map<String, Object> enableCaching =
importingClassMetadata.getAnnotationAttributes(EnableCaching.class.getName());
Assert.notNull(enableCaching,
"@EnableCaching is not present on importing class " +
importingClassMetadata.getClassName());
switch ((AdviceMode) enableCaching.get("mode")) {
case PROXY:
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean)enableCaching.get("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
return new String[] { ProxyCachingConfiguration.class.getName() };
case ASPECTJ:
return new String[] {"org.springframework.cache.aspectj.AspectJCachingConfiguration"};
default:
throw new IllegalArgumentException("Unknown AdviceMode " + enableCaching.get("mode"));
}
}
}

View File

@ -0,0 +1,61 @@
package org.springframework.cache.annotation;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
/**
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
* Configuration} classes annotated with @{@link EnableCaching} that wish or need to
* specify explicitly the {@link CacheManager} and {@link KeyGenerator} beans to be used
* for annotation-driven cache management.
*
* <p>See @{@link EnableCaching} for general examples and context; see
* {@link #cacheManager()} and {@link #keyGenerator()} for detailed instructions.
*
* @author Chris Beams
* @since 3.1
* @see EnableCaching
*/
public interface CachingConfigurer {
/**
* Return the cache manager bean to use for annotation-driven cache management.
* Implementations must explicitly declare
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
* <pre class="code">
* &#064;Configuration
* &#064;EnableCaching
* public class AppConfig implements CachingConfigurer {
* &#064;Bean // important!
* &#064;Override
* public CacheManager cacheManager() {
* // configure and return CacheManager instance
* }
* // ...
* }
* </pre>
* See @{@link EnableCaching} for more complete examples.
*/
CacheManager cacheManager();
/**
* Return the key generator bean to use for annotation-driven cache management.
* Implementations must explicitly declare
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
* <pre class="code">
* &#064;Configuration
* &#064;EnableCaching
* public class AppConfig implements CachingConfigurer {
* &#064;Bean // important!
* &#064;Override
* public KeyGenerator keyGenerator() {
* // configure and return KeyGenerator instance
* }
* // ...
* }
* </pre>
* See @{@link EnableCaching} for more complete examples.
*/
KeyGenerator keyGenerator();
}

View File

@ -0,0 +1,177 @@
/*
* Copyright 2002-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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
/**
* Enables Spring's annotation-driven cache management capability, similar to
* the support found in Spring's {@code <cache:*>} XML namespace. To be used together
* with @{@link org.springframework.context.annotation.Configuration Configuration}
* classes as follows:
* <pre class="code">
* &#064;Configuration
* &#064;EnableCaching
* public class AppConfig {
* &#064;Bean
* public MyService myService() {
* // configure and return a class having &#064;Cacheable methods
* return new MyService();
* }
*
* &#064;Bean
* public CacheManager cacheManager() {
* // configure and return an implementation of Spring's CacheManager SPI
* SimpleCacheManager cacheManager = new SimpleCacheManager();
* cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
* return cacheManager;
* }
* }</pre>
*
* <p>For reference, the example above can be compared to the following Spring XML
* configuration:
* <pre class="code">
* {@code
* <beans>
* <cache:annotation-driven/>
* <bean id="myService" class="com.foo.MyService"/>
* <bean id="cacheManager" 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>
* </beans>
* }</pre>
* In both of the scenarios above, {@code @EnableCaching} and {@code
* <cache:annotation-driven/>} are responsible for registering the necessary Spring
* components that power annotation-driven cache management, such as the
* {@link org.springframework.cache.interceptor.CacheInterceptor CacheInterceptor} and the
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} methods are invoked.
*
* <p><strong>A bean of type {@link org.springframework.cache.CacheManager CacheManager}
* must be registered</strong>, as there is no reasonable default that the framework can
* use as a convention. And whereas the {@code <cache:annotation-driven>} element assumes
* a bean <em>named</em> "cacheManager", {@code @EnableCaching} searches for a cache
* manager bean <em>by type</em>. Therefore, naming of the cache manager bean method is
* not significant.
*
* <p>For those that wish to establish a more direct relationship between
* {@code @EnableCaching} and the exact cache manager bean to be used,
* the {@link CachingConfigurer} callback interface may be implemented - notice the
* {@code implements} clause and the {@code @Override}-annotated methods below:
* <pre class="code">
* &#064;Configuration
* &#064;EnableCaching
* public class AppConfig implements CachingConfigurer {
* &#064;Bean
* public MyService myService() {
* // configure and return a class having &#064;Cacheable methods
* return new MyService();
* }
*
* &#064;Bean
* &#064;Override
* public CacheManager cacheManager() {
* // configure and return an implementation of Spring's CacheManager SPI
* SimpleCacheManager cacheManager = new SimpleCacheManager();
* cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
* return cacheManager;
* }
*
* &#064;Bean
* &#064;Override
* public KeyGenerator keyGenerator() {
* // configure and return an implementation of Spring's KeyGenerator SPI
* return new MyKeyGenerator();
* }
* }</pre>
* This approach may be desirable simply because it is more explicit, or it may be
* necessary in order to distinguish between two {@code CacheManager} beans present in the
* same container.
*
* <p>Notice also the {@code keyGenerator} method in the example above. This allows for
* customizing the strategy for cache key generation, per Spring's {@link
* org.springframework.cache.interceptor.KeyGenerator KeyGenerator} SPI. Normally,
* {@code @EnableCaching} will configure Spring's
* {@link org.springframework.cache.interceptor.DefaultKeyGenerator DefaultKeyGenerator}
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
* must be provided explicitly. Return {@code new DefaultKeyGenerator()} from this method
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
* details.
*
* <p>The {@link #mode()} attribute controls how advice is applied; if the mode is
* {@link AdviceMode#PROXY} (the default), then the other attributes such as
* {@link #proxyTargetClass()} control the behavior of the proxying.
*
* <p>If the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
* {@link #proxyTargetClass()} attribute is obsolete. Note also that in this case the
* {@code spring-aspects} module JAR must be present on the classpath.
*
* @author Chris Beams
* @since 3.1
* @see CachingConfigurer
* @see CachingConfigurationSelector
* @see ProxyCachingConfiguration
* @see org.springframework.cache.aspectj.AspectJCachingConfiguration
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
* to standard Java interface-based proxies. The default is {@code false}. <strong>
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
*
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Cacheable}. For example, other beans marked with Spring's
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
*/
boolean proxyTargetClass() default false;
/**
* Indicate how caching advice should be applied. The default is
* {@link AdviceMode.PROXY}.
* @see AdviceMode
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the caching advisor
* when multiple advices are applied at a specific joinpoint.
* The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-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 org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cache.interceptor.CacheOperationSource;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
/**
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
* to enable proxy-based annotation-driven cache management.
*
* @author Chris Beams
* @since 3.1
* @see EnableCaching
* @see CachingConfigurationSelector
*/
@Configuration
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
@Bean(name=AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
BeanFactoryCacheOperationSourceAdvisor advisor =
new BeanFactoryCacheOperationSourceAdvisor();
advisor.setCacheOperationSource(cacheOperationSource());
advisor.setAdvice(cacheInterceptor());
advisor.setOrder(((Integer)this.enableCaching.get("order")));
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheOperationSource cacheOperationSource() {
return new AnnotationCacheOperationSource();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public CacheInterceptor cacheInterceptor() {
CacheInterceptor interceptor = new CacheInterceptor();
interceptor.setCacheOperationSources(cacheOperationSource());
if (this.cacheManager != null) {
interceptor.setCacheManager(this.cacheManager);
}
if (this.keyGenerator != null) {
interceptor.setKeyGenerator(this.keyGenerator);
}
return interceptor;
}
}

View File

@ -0,0 +1,184 @@
/*
* 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.config;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.interceptor.CacheInterceptor;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Integration tests for @EnableCaching and its related @Configuration classes.
*
* @author Chris Beams
*/
public class EnableCachingTests extends AbstractAnnotationTests {
/** hook into superclass suite of tests */
@Override
protected ApplicationContext getApplicationContext() {
return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
}
@Test
public void testKeyStrategy() throws Exception {
CacheInterceptor ci = ctx.getBean(CacheInterceptor.class);
Assert.assertSame(ctx.getBean(KeyGenerator.class), ci.getKeyGenerator());
}
// --- local tests -------
@Test
public void singleCacheManagerBean() throws Throwable {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(SingleCacheManagerConfig.class);
ctx.refresh();
}
@Test(expected=IllegalStateException.class)
public void multipleCacheManagerBeans() throws Throwable {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MultiCacheManagerConfig.class);
try {
ctx.refresh();
} catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
assertTrue(root.getMessage().contains("beans of type CacheManager"));
throw root;
}
}
@Test
public void multipleCacheManagerBeans_implementsCachingConfigurer() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MultiCacheManagerConfigurer.class);
ctx.refresh(); // does not throw
}
@Test(expected=IllegalStateException.class)
public void multipleCachingConfigurers() throws Throwable {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
try {
ctx.refresh();
} catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
assertTrue(root.getMessage().contains("implementations of CachingConfigurer"));
throw root;
}
}
@Test(expected=IllegalStateException.class)
public void noCacheManagerBeans() throws Throwable {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(EmptyConfig.class);
try {
ctx.refresh();
} catch (BeanCreationException ex) {
Throwable root = ex.getRootCause();
assertTrue(root.getMessage().contains("No bean of type CacheManager"));
throw root;
}
}
@Configuration
@EnableCaching
static class EnableCachingConfig implements CachingConfigurer {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cm = new SimpleCacheManager();
cm.setCaches(Arrays.asList(
new ConcurrentMapCache("default"),
new ConcurrentMapCache("primary"),
new ConcurrentMapCache("secondary")));
return cm;
}
@Bean
public CacheableService<?> service() {
return new DefaultCacheableService();
}
@Bean
public CacheableService<?> classService() {
return new AnnotatedClassCacheableService();
}
@Bean
public KeyGenerator keyGenerator() {
return new SomeKeyGenerator();
}
}
@Configuration
@EnableCaching
static class EmptyConfig {
}
@Configuration
@EnableCaching
static class SingleCacheManagerConfig {
@Bean
public CacheManager cm1() { return new NoOpCacheManager(); }
}
@Configuration
@EnableCaching
static class MultiCacheManagerConfig {
@Bean
public CacheManager cm1() { return new NoOpCacheManager(); }
@Bean
public CacheManager cm2() { return new NoOpCacheManager(); }
}
@Configuration
@EnableCaching
static class MultiCacheManagerConfigurer implements CachingConfigurer {
@Bean
public CacheManager cm1() { return new NoOpCacheManager(); }
@Bean
public CacheManager cm2() { return new NoOpCacheManager(); }
public CacheManager cacheManager() {
return cm1();
}
public KeyGenerator keyGenerator() {
return null;
}
}
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2002-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 static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
import org.springframework.cache.support.NoOpCacheManager;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Repository;
/**
* Integration tests for the @EnableCaching annotation.
*
* @author Chris Beams
* @since 3.1
*/
public class EnableCachingIntegrationTests {
@Test
public void repositoryIsClassBasedCacheProxy() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, ProxyTargetClassCachingConfig.class);
ctx.refresh();
assertCacheProxying(ctx);
assertThat(AopUtils.isCglibProxy(ctx.getBean(FooRepository.class)), is(true));
}
@Test
public void repositoryUsesAspectJAdviceMode() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(Config.class, AspectJCacheConfig.class);
try {
ctx.refresh();
} catch (Exception ex) {
// this test is a bit fragile, but gets the job done, proving that an
// attempt was made to look up the AJ aspect. It's due to classpath issues
// in .integration-tests that it's not found.
assertTrue(ex.getMessage().endsWith("AspectJCachingConfiguration.class] cannot be opened because it does not exist"));
}
}
private void assertCacheProxying(AnnotationConfigApplicationContext ctx) {
FooRepository repo = ctx.getBean(FooRepository.class);
boolean isCacheProxy = false;
if (AopUtils.isAopProxy(repo)) {
for (Advisor advisor : ((Advised)repo).getAdvisors()) {
if (advisor instanceof BeanFactoryCacheOperationSourceAdvisor) {
isCacheProxy = true;
break;
}
}
}
assertTrue("FooRepository is not a cache proxy", isCacheProxy);
}
@Configuration
@EnableCaching(proxyTargetClass=true)
static class ProxyTargetClassCachingConfig {
@Bean
CacheManager mgr() {
return new NoOpCacheManager();
}
}
@Configuration
static class Config {
@Bean
FooRepository fooRepository() {
return new DummyFooRepository();
}
}
@Configuration
@EnableCaching(mode=AdviceMode.ASPECTJ)
static class AspectJCacheConfig {
@Bean
CacheManager cacheManager() {
return new NoOpCacheManager();
}
}
interface FooRepository {
List<Object> findAll();
}
@Repository
static class DummyFooRepository implements FooRepository {
@Cacheable("primary")
public List<Object> findAll() {
return Collections.emptyList();
}
}
}