diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java index 0a1c1e58d48..cff8a0dd1fa 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java @@ -66,7 +66,7 @@ import org.springframework.util.Assert; @EnableConfigurationProperties(CacheProperties.class) @AutoConfigureBefore(HibernateJpaAutoConfiguration.class) @AutoConfigureAfter({ HazelcastAutoConfiguration.class, RedisAutoConfiguration.class }) -@Import(CacheConfigurationImportSelector.class) +@Import({ CacheManagerCustomizerInvoker.class, CacheConfigurationImportSelector.class }) public class CacheAutoConfiguration { static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator"; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizer.java new file mode 100644 index 00000000000..9ea7c52648e --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizer.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2016 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.boot.autoconfigure.cache; + +import org.springframework.cache.CacheManager; + +/** + * Callback interface that can be implemented by beans wishing to customize the cache + * manager before it is fully initialized, in particular to tune its configuration. + * + * @author Stephane Nicoll + * @since 1.3.3 + */ +public interface CacheManagerCustomizer { + + /** + * Customize the cache manager. + * @param cacheManager the {@code CacheManager} to customize + */ + void customize(C cacheManager); + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvoker.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvoker.java new file mode 100644 index 00000000000..13f03a1738b --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvoker.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2016 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.boot.autoconfigure.cache; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.cache.CacheManager; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.GenericTypeResolver; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; + +/** + * Invoke the available {@link CacheManagerCustomizer} instances in the context for a + * given {@link CacheManager}. + * + * @author Stephane Nicoll + */ +class CacheManagerCustomizerInvoker implements ApplicationContextAware { + + private ConfigurableApplicationContext applicationContext; + + /** + * Customize the specified {@link CacheManager}. Locates all {@link CacheManagerCustomizer} + * beans able to handle the specified instance and invoke + * {@link CacheManagerCustomizer#customize(CacheManager)} on them. + * @param cacheManager the cache manager to customize + */ + public void customize(CacheManager cacheManager) { + List> customizers = findCustomizers(cacheManager); + AnnotationAwareOrderComparator.sort(customizers); + for (CacheManagerCustomizer customizer : customizers) { + customizer.customize(cacheManager); + } + } + + @SuppressWarnings("unchecked") + private List> findCustomizers(CacheManager cacheManager) { + if (this.applicationContext == null) { + return Collections.emptyList(); + } + Map map = BeanFactoryUtils + .beansOfTypeIncludingAncestors(this.applicationContext.getBeanFactory(), CacheManagerCustomizer.class); + List> customizers + = new ArrayList>(); + for (CacheManagerCustomizer customizer : map.values()) { + Class target = GenericTypeResolver.resolveTypeArgument( + customizer.getClass(), CacheManagerCustomizer.class); + if (target == null || target.isAssignableFrom(cacheManager.getClass())) { + customizers.add(customizer); + } + } + return customizers; + } + + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + if (applicationContext instanceof ConfigurableApplicationContext) { + this.applicationContext = (ConfigurableApplicationContext) applicationContext; + } + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/EhCacheCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/EhCacheCacheConfiguration.java index 14a02b4628a..a76b0fd470f 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/EhCacheCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/EhCacheCacheConfiguration.java @@ -48,9 +48,14 @@ class EhCacheCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) { - return new EhCacheCacheManager(ehCacheCacheManager); + EhCacheCacheManager cacheManager = new EhCacheCacheManager(ehCacheCacheManager); + this.customizerInvoker.customize(cacheManager); + return cacheManager; } @Bean diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.java index 244122e2efb..052f3a55f4b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.cache; import java.util.Collection; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cache.Cache; @@ -40,10 +41,14 @@ import org.springframework.context.annotation.Configuration; @Conditional(CacheCondition.class) class GenericCacheConfiguration { + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public SimpleCacheManager cacheManager(Collection caches) { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(caches); + this.customizerInvoker.customize(cacheManager); return cacheManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GuavaCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GuavaCacheConfiguration.java index 2973e881f04..5bb17c9ab0e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GuavaCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/GuavaCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -48,6 +48,9 @@ class GuavaCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Autowired(required = false) private CacheBuilder cacheBuilder; @@ -64,6 +67,7 @@ class GuavaCacheConfiguration { if (!CollectionUtils.isEmpty(cacheNames)) { cacheManager.setCacheNames(cacheNames); } + this.customizerInvoker.customize(cacheManager); return cacheManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/HazelcastInstanceConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/HazelcastInstanceConfiguration.java index c229d018299..a15ebbb61e5 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/HazelcastInstanceConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/HazelcastInstanceConfiguration.java @@ -48,6 +48,9 @@ abstract class HazelcastInstanceConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public HazelcastCacheManager cacheManager( HazelcastInstance existingHazelcastInstance) throws IOException { @@ -58,7 +61,9 @@ abstract class HazelcastInstanceConfiguration { location).getHazelcastInstance(); return new CloseableHazelcastCacheManager(cacheHazelcastInstance); } - return new HazelcastCacheManager(existingHazelcastInstance); + HazelcastCacheManager cacheManager = new HazelcastCacheManager(existingHazelcastInstance); + this.customizerInvoker.customize(cacheManager); + return cacheManager; } } @@ -70,6 +75,9 @@ abstract class HazelcastInstanceConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public HazelcastInstance hazelcastInstance() throws IOException { Resource config = this.cacheProperties.getHazelcast().getConfig(); @@ -82,7 +90,9 @@ abstract class HazelcastInstanceConfiguration { @Bean public HazelcastCacheManager cacheManager() throws IOException { - return new HazelcastCacheManager(hazelcastInstance()); + HazelcastCacheManager cacheManager = new HazelcastCacheManager(hazelcastInstance()); + this.customizerInvoker.customize(cacheManager); + return cacheManager; } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/InfinispanCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/InfinispanCacheConfiguration.java index da0040aa56f..8af92877704 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/InfinispanCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/InfinispanCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -51,13 +51,18 @@ public class InfinispanCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Autowired(required = false) private ConfigurationBuilder defaultConfigurationBuilder; @Bean public SpringEmbeddedCacheManager cacheManager( EmbeddedCacheManager embeddedCacheManager) { - return new SpringEmbeddedCacheManager(embeddedCacheManager); + SpringEmbeddedCacheManager cacheManager = new SpringEmbeddedCacheManager(embeddedCacheManager); + this.customizerInvoker.customize(cacheManager); + return cacheManager; } @Bean(destroyMethod = "stop") diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.java index d8653abedbc..0d44a30caf6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/JCacheCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -63,6 +63,9 @@ class JCacheCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Autowired(required = false) private javax.cache.configuration.Configuration defaultCacheConfiguration; @@ -71,7 +74,9 @@ class JCacheCacheConfiguration { @Bean public JCacheCacheManager cacheManager(CacheManager jCacheCacheManager) { - return new JCacheCacheManager(jCacheCacheManager); + JCacheCacheManager cacheManager = new JCacheCacheManager(jCacheCacheManager); + this.customizerInvoker.customize(cacheManager); + return cacheManager; } @Bean diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java index 9e370f9668c..56c1bb6f6d7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -46,6 +46,9 @@ class RedisCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public RedisCacheManager cacheManager(RedisTemplate redisTemplate) { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); @@ -53,6 +56,7 @@ class RedisCacheConfiguration { if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } + this.customizerInvoker.customize(cacheManager); return cacheManager; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.java index 4350ee73bd4..419094a96ae 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -40,6 +40,9 @@ class SimpleCacheConfiguration { @Autowired private CacheProperties cacheProperties; + @Autowired + CacheManagerCustomizerInvoker customizerInvoker; + @Bean public ConcurrentMapCacheManager cacheManager() { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); @@ -47,6 +50,7 @@ class SimpleCacheConfiguration { if (!cacheNames.isEmpty()) { cacheManager.setCacheNames(cacheNames); } + this.customizerInvoker.customize(cacheManager); return cacheManager; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java index 91b73e3bdb4..07defbbda8c 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java @@ -17,8 +17,12 @@ package org.springframework.boot.autoconfigure.cache; import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import javax.cache.configuration.CompleteConfiguration; import javax.cache.configuration.MutableConfiguration; @@ -140,6 +144,12 @@ public class CacheAutoConfigurationTests { assertThat(cacheManager.getCacheNames()).isEmpty(); } + @Test + public void simpleCacheWithCustomizers() { + testCustomizers(DefaultCacheAndCustomizersConfiguration.class, "simple", + "allCacheManagerCustomizer", "simpleCacheManagerCustomizer"); + } + @Test public void simpleCacheExplicitWithCacheNames() { load(DefaultCacheConfiguration.class, "spring.cache.type=simple", @@ -168,6 +178,12 @@ public class CacheAutoConfigurationTests { load(DefaultCacheConfiguration.class, "spring.cache.type=generic"); } + @Test + public void genericCacheWithCustomizers() { + testCustomizers(GenericCacheAndCustomizersConfiguration.class, "generic", + "allCacheManagerCustomizer", "genericCacheManagerCustomizer"); + } + @Test public void genericCacheExplicitWithCaches() { load(GenericCacheConfiguration.class, "spring.cache.type=generic"); @@ -186,6 +202,12 @@ public class CacheAutoConfigurationTests { assertThat(cacheManager.getCacheNames()).isEmpty(); } + @Test + public void redisCacheWithCustomizers() { + testCustomizers(RedisCacheAndCustomizersConfiguration.class, "redis", + "allCacheManagerCustomizer", "redisCacheManagerCustomizer"); + } + @Test public void redisCacheExplicitWithCaches() { load(RedisCacheConfiguration.class, "spring.cache.type=redis", @@ -298,6 +320,12 @@ public class CacheAutoConfigurationTests { .isEqualTo(cacheManager.getCacheManager()); } + @Test + public void ehCacheCacheWithCustomizers() { + testCustomizers(DefaultCacheAndCustomizersConfiguration.class, "ehcache", + "allCacheManagerCustomizer", "ehCacheCacheManagerCustomizer"); + } + @Test public void ehCacheCacheWithConfig() { load(DefaultCacheConfiguration.class, "spring.cache.type=ehcache", @@ -329,6 +357,12 @@ public class CacheAutoConfigurationTests { .isEqualTo(getHazelcastInstance(cacheManager)); } + @Test + public void hazelcastCacheWithCustomizers() { + testCustomizers(DefaultCacheAndCustomizersConfiguration.class, "hazelcast", + "allCacheManagerCustomizer", "hazelcastCacheManagerCustomizer"); + } + @Test public void hazelcastCacheWithConfig() throws IOException { load(DefaultCacheConfiguration.class, "spring.cache.type=hazelcast", @@ -441,6 +475,12 @@ public class CacheAutoConfigurationTests { assertThat(cacheManager.getCacheNames()).contains("foo", "bar"); } + @Test + public void infinispanCacheWithCustomizers() { + testCustomizers(DefaultCacheAndCustomizersConfiguration.class, "infinispan", + "allCacheManagerCustomizer", "infinispanCacheManagerCustomizer"); + } + @Test public void infinispanCacheWithCaches() { load(DefaultCacheConfiguration.class, "spring.cache.type=infinispan", @@ -508,6 +548,12 @@ public class CacheAutoConfigurationTests { assertThat(((GuavaCache) foo).getNativeCache().stats().missCount()).isEqualTo(0L); } + @Test + public void guavaCacheWithCustomizers() { + testCustomizers(DefaultCacheAndCustomizersConfiguration.class, "guava", + "allCacheManagerCustomizer", "guavaCacheManagerCustomizer"); + } + @Test public void guavaCacheExplicitWithSpec() { load(DefaultCacheConfiguration.class, "spring.cache.type=guava", @@ -537,6 +583,25 @@ public class CacheAutoConfigurationTests { return type.cast(cacheManager); } + private void testCustomizers(Class config, String cacheType, String... expectedCustomizerNames) { + load(config, "spring.cache.type=" + cacheType); + CacheManager cacheManager = validateCacheManager(CacheManager.class); + List expected = new ArrayList(); + expected.addAll(Arrays.asList(expectedCustomizerNames)); + Map map = + this.context.getBeansOfType(CacheManagerTestCustomizer.class); + for (Map.Entry entry : map.entrySet()) { + if (expected.contains(entry.getKey())) { + expected.remove(entry.getKey()); + assertThat(entry.getValue().cacheManager).isSameAs(cacheManager); + } + else { + assertThat(entry.getValue().cacheManager).isNull(); + } + } + assertThat(expected).hasSize(0); + } + private void load(Class config, String... environment) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(applicationContext, environment); @@ -562,6 +627,13 @@ public class CacheAutoConfigurationTests { } + @Configuration + @EnableCaching + @Import(CacheManagerCustomizersConfiguration.class) + static class DefaultCacheAndCustomizersConfiguration { + + } + @Configuration @EnableCaching static class GenericCacheConfiguration { @@ -578,6 +650,11 @@ public class CacheAutoConfigurationTests { } + @Configuration + @Import({GenericCacheConfiguration.class, CacheManagerCustomizersConfiguration.class}) + static class GenericCacheAndCustomizersConfiguration { + } + @Configuration @EnableCaching static class RedisCacheConfiguration { @@ -589,6 +666,12 @@ public class CacheAutoConfigurationTests { } + @Configuration + @Import({RedisCacheConfiguration.class, CacheManagerCustomizersConfiguration.class}) + static class RedisCacheAndCustomizersConfiguration { + + } + @Configuration @EnableCaching static class JCacheCustomConfiguration { @@ -662,8 +745,8 @@ public class CacheAutoConfigurationTests { } @Configuration - @ImportAutoConfiguration({ CacheAutoConfiguration.class, - HazelcastAutoConfiguration.class }) + @ImportAutoConfiguration({CacheAutoConfiguration.class, + HazelcastAutoConfiguration.class}) static class HazelcastAndCacheConfiguration { } @@ -682,7 +765,7 @@ public class CacheAutoConfigurationTests { } @Configuration - @Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class }) + @Import({GenericCacheConfiguration.class, RedisCacheConfiguration.class}) static class CustomCacheManagerConfiguration { @Bean @@ -693,7 +776,7 @@ public class CacheAutoConfigurationTests { } @Configuration - @Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class }) + @Import({GenericCacheConfiguration.class, RedisCacheConfiguration.class}) static class CustomCacheManagerFromSupportConfiguration extends CachingConfigurerSupport { @@ -718,7 +801,7 @@ public class CacheAutoConfigurationTests { } @Configuration - @Import({ GenericCacheConfiguration.class, RedisCacheConfiguration.class }) + @Import({GenericCacheConfiguration.class, RedisCacheConfiguration.class}) static class CustomCacheResolverConfiguration extends CachingConfigurerSupport { @Override @@ -739,4 +822,64 @@ public class CacheAutoConfigurationTests { } + @Configuration + static class CacheManagerCustomizersConfiguration { + + @Bean + public CacheManagerCustomizer allCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { + }; + } + + @Bean + public CacheManagerCustomizer simpleCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { + }; + } + + @Bean + public CacheManagerCustomizer genericCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + @Bean + public CacheManagerCustomizer redisCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + @Bean + public CacheManagerCustomizer ehCacheCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + @Bean + public CacheManagerCustomizer hazelcastCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + @Bean + public CacheManagerCustomizer infinispanCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + @Bean + public CacheManagerCustomizer guavaCacheManagerCustomizer() { + return new CacheManagerTestCustomizer() { }; + } + + } + + static abstract class CacheManagerTestCustomizer implements CacheManagerCustomizer { + + private C cacheManager; + + @Override + public void customize(C cacheManager) { + if (this.cacheManager != null) { + throw new IllegalStateException("Customized invoked twice"); + } + this.cacheManager = cacheManager; + } + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvokerTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvokerTests.java new file mode 100644 index 00000000000..2d14038fd0b --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheManagerCustomizerInvokerTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2016 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.boot.autoconfigure.cache; + +import java.util.Arrays; + +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.concurrent.ConcurrentMapCacheManager; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * + * @author Stephane Nicoll + */ +public class CacheManagerCustomizerInvokerTests { + + private AnnotationConfigApplicationContext context; + + @After + public void tearDown() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void customizeSimpleCacheManager() { + load(SimpleConfiguration.class, "spring.cache.type=simple"); + ConcurrentMapCacheManager cacheManager = this.context.getBean(ConcurrentMapCacheManager.class); + assertThat(cacheManager.getCacheNames()).containsOnly("one", "two"); + } + + @Test + public void customizeNoConfigurableApplicationContext() { + CacheManagerCustomizerInvoker invoker = new CacheManagerCustomizerInvoker(); + ApplicationContext context = mock(ApplicationContext.class); + invoker.setApplicationContext(context); + invoker.customize(mock(CacheManager.class)); + verifyZeroInteractions(context); + } + + + private void load(Class config, String... environment) { + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(applicationContext, environment); + applicationContext.register(config); + applicationContext.register(CacheAutoConfiguration.class); + applicationContext.refresh(); + this.context = applicationContext; + } + + + @Configuration + @EnableCaching + static class SimpleConfiguration { + + @Bean + public CacheManagerCustomizer cacheManagerCustomizer() { + return new CacheManagerCustomizer() { + @Override + public void customize(ConcurrentMapCacheManager cacheManager) { + cacheManager.setCacheNames(Arrays.asList("one", "two")); + } + }; + } + } +} diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index cbe50c3aa0a..4c71c0a0b40 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3255,6 +3255,29 @@ Spring Boot tries to detect the following providers (in this order): It is also possible to _force_ the cache provider to use via the `spring.cache.type` property. +If the `CacheManager` is auto-configured by Spring Boot, you can further tune its +configuration before it is fully initialized by exposing a bean implementing the +`CacheManagerCustomizer` interface. The following set the cache names to use. + +[source,java,indent=0] +---- + @Bean + public CacheManagerCustomizer cacheManagerCustomizer() { + return new CacheManagerCustomizer() { + @Override + public void customize(ConcurrentMapCacheManager cacheManager) { + cacheManager.setCacheNames(Arrays.asList("one", "two")); + } + }; + } +---- + +[NOTE] +=== +In the example above, a `ConcurrentMapCacheManager` is expected to be configured. If that +is not the case, the customizer won't be invoked at all. You can have as many customizers +as you want and you can also order them as usual using `@Order` or `Ordered`. +=== [[boot-features-caching-provider-generic]]