diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java index b8381eb320b..8ea3eb0df3f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.java @@ -18,14 +18,8 @@ package org.springframework.boot.autoconfigure.cache; import java.util.List; -import javax.annotation.PostConstruct; - -import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -46,7 +40,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; -import org.springframework.context.annotation.Role; import org.springframework.core.type.AnnotationMetadata; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -73,8 +66,6 @@ import org.springframework.util.Assert; @Import(CacheConfigurationImportSelector.class) public class CacheAutoConfiguration { - static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator"; - @Bean @ConditionalOnMissingBean public CacheManagerCustomizers cacheManagerCustomizers( @@ -83,14 +74,10 @@ public class CacheAutoConfiguration { } @Bean - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() { - return new CacheManagerValidatorPostProcessor(); - } - - @Bean(name = VALIDATOR_BEAN_NAME) - public CacheManagerValidator cacheAutoConfigurationValidator() { - return new CacheManagerValidator(); + public CacheManagerValidator cacheAutoConfigurationValidator( + CacheProperties cacheProperties, + ObjectProvider cacheManager) { + return new CacheManagerValidator(cacheProperties, cacheManager); } @Configuration @@ -105,50 +92,25 @@ public class CacheAutoConfiguration { } - /** - * {@link BeanFactoryPostProcessor} to ensure that the {@link CacheManagerValidator} - * is triggered before {@link CacheAspectSupport} but without causing early - * instantiation. - */ - static class CacheManagerValidatorPostProcessor implements BeanFactoryPostProcessor { - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) - throws BeansException { - for (String name : beanFactory.getBeanNamesForType(CacheAspectSupport.class, - false, false)) { - BeanDefinition definition = beanFactory.getBeanDefinition(name); - definition.setDependsOn( - append(definition.getDependsOn(), VALIDATOR_BEAN_NAME)); - } - } - - private String[] append(String[] array, String value) { - String[] result = new String[array != null ? array.length + 1 : 1]; - if (array != null) { - System.arraycopy(array, 0, result, 0, array.length); - } - result[result.length - 1] = value; - return result; - } - - } - /** * Bean used to validate that a CacheManager exists and provide a more meaningful * exception. */ - static class CacheManagerValidator { + static class CacheManagerValidator implements InitializingBean { - @Autowired - private CacheProperties cacheProperties; + private final CacheProperties cacheProperties; - @Autowired(required = false) - private CacheManager cacheManager; + private final ObjectProvider cacheManager; - @PostConstruct - public void checkHasCacheManager() { - Assert.notNull(this.cacheManager, + CacheManagerValidator(CacheProperties cacheProperties, + ObjectProvider cacheManager) { + this.cacheProperties = cacheProperties; + this.cacheManager = cacheManager; + } + + @Override + public void afterPropertiesSet() { + Assert.notNull(this.cacheManager.getIfAvailable(), () -> "No cache manager could " + "be auto-configured, check your configuration (caching " + "type is '" + this.cacheProperties.getType() + "')"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java index 0d517901c48..7b69c6e8a88 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/cache/CacheAutoConfigurationTests.java @@ -16,7 +16,9 @@ package org.springframework.boot.autoconfigure.cache; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import javax.cache.Caching; import javax.cache.configuration.CompleteConfiguration; @@ -43,6 +45,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; @@ -745,6 +748,21 @@ public class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationT .run(this::validateCaffeineCacheWithStats); } + @Test + public void autoConfiguredCacheManagerCanBeSwapped() { + this.contextRunner + .withUserConfiguration(CacheManagerPostProcessorConfiguration.class) + .withPropertyValues("spring.cache.type=caffeine") + .run((context) -> { + getCacheManager(context, SimpleCacheManager.class); + CacheManagerPostProcessor postProcessor = context.getBean( + CacheManagerPostProcessor.class); + assertThat(postProcessor.cacheManagers).hasSize(1); + assertThat(postProcessor.cacheManagers.get(0)) + .isInstanceOf(CaffeineCacheManager.class); + }); + } + private void validateCaffeineCacheWithStats(AssertableApplicationContext context) { CaffeineCacheManager manager = getCacheManager(context, CaffeineCacheManager.class); @@ -1009,4 +1027,37 @@ public class CacheAutoConfigurationTests extends AbstractCacheAutoConfigurationT } + @Configuration + @EnableCaching + static class CacheManagerPostProcessorConfiguration { + + @Bean + public static BeanPostProcessor cacheManagerBeanPostProcessor() { + return new CacheManagerPostProcessor(); + } + + } + + private static class CacheManagerPostProcessor implements BeanPostProcessor { + + private final List cacheManagers = new ArrayList<>(); + + @Override + public Object postProcessBeforeInitialization(Object bean, + String beanName) { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, + String beanName) { + if (bean instanceof CacheManager) { + this.cacheManagers.add((CacheManager) bean); + return new SimpleCacheManager(); + } + return bean; + } + + } + }