Commit c230a21a authored by Stephane Nicoll's avatar Stephane Nicoll

Polish CacheManagerCustomizers

Closes gh-7792
parent 2f3571f1
...@@ -16,9 +16,12 @@ ...@@ -16,9 +16,12 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import java.util.List;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
...@@ -67,11 +70,18 @@ import org.springframework.util.Assert; ...@@ -67,11 +70,18 @@ import org.springframework.util.Assert;
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class) @AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, @AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
RedisAutoConfiguration.class }) RedisAutoConfiguration.class })
@Import({ CacheManagerCustomizers.class, CacheConfigurationImportSelector.class }) @Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration { public class CacheAutoConfiguration {
static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator"; static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator";
@Bean
@ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers(
ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
return new CacheManagerCustomizers(customizers.getIfAvailable());
}
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() { public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() {
......
...@@ -17,33 +17,34 @@ ...@@ -17,33 +17,34 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext; import org.springframework.core.ResolvableType;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
/** /**
* Invokes the available {@link CacheManagerCustomizer} instances in the context for a * Invokes the available {@link CacheManagerCustomizer} instances in the context for a
* given {@link CacheManager}. * given {@link CacheManager}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.5.0
*/ */
class CacheManagerCustomizers implements ApplicationContextAware { public class CacheManagerCustomizers {
private static final Log logger = LogFactory.getLog(CacheManagerCustomizers.class); private static final Log logger = LogFactory.getLog(CacheManagerCustomizers.class);
private ConfigurableApplicationContext applicationContext; private final List<CacheManagerCustomizer<?>> customizers;
public CacheManagerCustomizers(
List<? extends CacheManagerCustomizer<?>> customizers) {
this.customizers = (customizers != null ?
new ArrayList<CacheManagerCustomizer<?>>(customizers) :
Collections.<CacheManagerCustomizer<?>>emptyList());
}
/** /**
* Customize the specified {@link CacheManager}. Locates all * Customize the specified {@link CacheManager}. Locates all
...@@ -54,18 +55,20 @@ class CacheManagerCustomizers implements ApplicationContextAware { ...@@ -54,18 +55,20 @@ class CacheManagerCustomizers implements ApplicationContextAware {
* @return the cache manager * @return the cache manager
*/ */
public <T extends CacheManager> T customize(T cacheManager) { public <T extends CacheManager> T customize(T cacheManager) {
List<CacheManagerCustomizer<CacheManager>> customizers = findCustomizers( for (CacheManagerCustomizer<?> customizer : this.customizers) {
cacheManager); Class<?> generic = ResolvableType
AnnotationAwareOrderComparator.sort(customizers); .forClass(CacheManagerCustomizer.class,
for (CacheManagerCustomizer<CacheManager> customizer : customizers) { customizer.getClass())
.resolveGeneric();
if (generic.isInstance(cacheManager)) {
customize(cacheManager, customizer); customize(cacheManager, customizer);
} }
}
return cacheManager; return cacheManager;
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
private void customize(CacheManager cacheManager, private void customize(CacheManager cacheManager, CacheManagerCustomizer customizer) {
CacheManagerCustomizer customizer) {
try { try {
customizer.customize(cacheManager); customizer.customize(cacheManager);
} }
...@@ -79,40 +82,4 @@ class CacheManagerCustomizers implements ApplicationContextAware { ...@@ -79,40 +82,4 @@ class CacheManagerCustomizers implements ApplicationContextAware {
} }
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<CacheManagerCustomizer<CacheManager>> findCustomizers(
CacheManager cacheManager) {
if (this.applicationContext == null) {
return Collections.emptyList();
}
Class<?> cacheManagerClass = cacheManager.getClass();
List<CacheManagerCustomizer<CacheManager>> customizers = new ArrayList<CacheManagerCustomizer<CacheManager>>();
for (CacheManagerCustomizer customizer : getBeans(CacheManagerCustomizer.class)) {
if (canCustomize(customizer, cacheManagerClass)) {
customizers.add(customizer);
}
}
return customizers;
}
private <T> Collection<T> getBeans(Class<T> type) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(
this.applicationContext.getBeanFactory(), type).values();
}
private boolean canCustomize(CacheManagerCustomizer<?> customizer,
Class<?> cacheManagerClass) {
Class<?> target = GenericTypeResolver.resolveTypeArgument(customizer.getClass(),
CacheManagerCustomizer.class);
return (target == null || target.isAssignableFrom(cacheManagerClass));
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
if (applicationContext instanceof ConfigurableApplicationContext) {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
}
} }
...@@ -16,80 +16,86 @@ ...@@ -16,80 +16,86 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.springframework.boot.test.util.EnvironmentTestUtils;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class CacheManagerCustomizersTests { public class CacheManagerCustomizersTests {
private AnnotationConfigApplicationContext context; @Test
public void customizeWithNullCustomizersShouldDoNothing() {
@After new CacheManagerCustomizers(null)
public void tearDown() { .customize(mock(CacheManager.class));
if (this.context != null) {
this.context.close();
}
} }
@Test @Test
public void customizeSimpleCacheManager() { public void customizeSimpleCacheManager() {
load(SimpleConfiguration.class, "spring.cache.type=simple"); CacheManagerCustomizers customizers = new CacheManagerCustomizers(
ConcurrentMapCacheManager cacheManager = this.context Collections.singletonList(new CacheNamesCacheManagerCustomizer()));
.getBean(ConcurrentMapCacheManager.class); ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
customizers.customize(cacheManager);
assertThat(cacheManager.getCacheNames()).containsOnly("one", "two"); assertThat(cacheManager.getCacheNames()).containsOnly("one", "two");
} }
@Test @Test
public void customizeNoConfigurableApplicationContext() { public void customizeShouldCheckGeneric() throws Exception {
CacheManagerCustomizers invoker = new CacheManagerCustomizers(); List<TestCustomizer<?>> list = new ArrayList<TestCustomizer<?>>();
ApplicationContext context = mock(ApplicationContext.class); list.add(new TestCustomizer<CacheManager>());
invoker.setApplicationContext(context); list.add(new TestConcurrentMapCacheManagerCustomizer());
invoker.customize(mock(CacheManager.class)); CacheManagerCustomizers customizers = new CacheManagerCustomizers(list);
verifyZeroInteractions(context); customizers.customize(mock(CacheManager.class));
assertThat(list.get(0).getCount()).isEqualTo(1);
assertThat(list.get(1).getCount()).isEqualTo(0);
customizers.customize(mock(ConcurrentMapCacheManager.class));
assertThat(list.get(0).getCount()).isEqualTo(2);
assertThat(list.get(1).getCount()).isEqualTo(1);
customizers.customize(mock(CaffeineCacheManager.class));
assertThat(list.get(0).getCount()).isEqualTo(3);
assertThat(list.get(1).getCount()).isEqualTo(1);
}
static class CacheNamesCacheManagerCustomizer
implements CacheManagerCustomizer<ConcurrentMapCacheManager> {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(Arrays.asList("one", "two"));
} }
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 private static class TestCustomizer<T extends CacheManager>
@EnableCaching implements CacheManagerCustomizer<T> {
static class SimpleConfiguration {
@Bean private int count;
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override @Override
public void customize(ConcurrentMapCacheManager cacheManager) { public void customize(T cacheManager) {
cacheManager.setCacheNames(Arrays.asList("one", "two")); this.count++;
} }
}; public int getCount() {
return this.count;
} }
} }
private static class TestConcurrentMapCacheManagerCustomizer
extends TestCustomizer<ConcurrentMapCacheManager> {
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment