8 changed files with 852 additions and 0 deletions
@ -0,0 +1,50 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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(); |
||||
|
||||
} |
||||
@ -0,0 +1,177 @@
@@ -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 @@
@@ -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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,184 @@
@@ -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 @@
@@ -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