Introduce @EnableCaching
See EnableCaching Javadoc for details. Issue: SPR-8312
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user