From 1314aaa3680dbf86afae2d57f16ec233606eaa94 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 3 May 2018 16:55:26 +0200 Subject: [PATCH] Avoid early init of CacheManager This commit restructures the Cache auto-configuration to avoid an early init on CacheManager (and potentially all its infrastructure). Rather than adding a dependency on the validator bean, this commit relies on the fact CacheAspectSupport checks if a CacheManager is available in the afterSingletonsInstantiated callback. In this case, a simple bean with a postconstruct callback is enough. Closes gh-13038 --- .../cache/CacheAutoConfiguration.java | 74 +++++-------------- .../cache/CacheAutoConfigurationTests.java | 48 +++++++++++- 2 files changed, 65 insertions(+), 57 deletions(-) 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 2395a0a4b3..2c29fe8525 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2018 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,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 { + + private final CacheProperties cacheProperties; - @Autowired - private CacheProperties cacheProperties; + private final ObjectProvider cacheManager; - @Autowired(required = false) - private CacheManager cacheManager; + CacheManagerValidator(CacheProperties cacheProperties, + ObjectProvider cacheManager) { + this.cacheProperties = cacheProperties; + this.cacheManager = cacheManager; + } - @PostConstruct - public void checkHasCacheManager() { - Assert.notNull(this.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-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 292166049f..73a7f8f945 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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,9 +51,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.springframework.beans.BeansException; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.cache.support.MockCachingProvider; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; @@ -770,6 +772,17 @@ public class CacheAutoConfigurationTests { validateCaffeineCacheWithStats(); } + @Test + public void autoConfiguredCacheManagerCanBeSwapped() { + load(CacheManagerPostProcessorConfiguration.class, "spring.cache.type=caffeine"); + validateCacheManager(SimpleCacheManager.class); + CacheManagerPostProcessor postProcessor = this.context.getBean( + CacheManagerPostProcessor.class); + assertThat(postProcessor.cacheManagers).hasSize(1); + assertThat(postProcessor.cacheManagers.get(0)) + .isInstanceOf(CaffeineCacheManager.class); + } + private void validateCaffeineCacheWithStats() { CaffeineCacheManager cacheManager = validateCacheManager( CaffeineCacheManager.class); @@ -1164,4 +1177,37 @@ public class CacheAutoConfigurationTests { } + @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) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, + String beanName) throws BeansException { + if (bean instanceof CacheManager) { + this.cacheManagers.add((CacheManager) bean); + return new SimpleCacheManager(); + } + return bean; + } + + } + }