Introduce @EnableCaching
See EnableCaching Javadoc for details. Issue: SPR-8312
This commit is contained in:
parent
732bf58570
commit
4f3cbb45f4
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean // important!
|
||||
* @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">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public KeyGenerator keyGenerator() {
|
||||
* // configure and return KeyGenerator instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See @{@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
KeyGenerator keyGenerator();
|
||||
|
||||
}
|
177
org.springframework.context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
vendored
Normal file
177
org.springframework.context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
vendored
Normal 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">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* @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">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @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;
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
184
org.springframework.context/src/test/java/org/springframework/cache/config/EnableCachingTests.java
vendored
Normal file
184
org.springframework.context/src/test/java/org/springframework/cache/config/EnableCachingTests.java
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue