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
This commit is contained in:
Stephane Nicoll 2018-05-03 16:55:26 +02:00
parent 7392c57343
commit 1314aaa368
2 changed files with 65 additions and 57 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.List;
import javax.annotation.PostConstruct; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider; 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.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 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.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector; import org.springframework.context.annotation.ImportSelector;
import org.springframework.context.annotation.Role;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
@ -73,8 +66,6 @@ import org.springframework.util.Assert;
@Import(CacheConfigurationImportSelector.class) @Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration { public class CacheAutoConfiguration {
static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator";
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public CacheManagerCustomizers cacheManagerCustomizers( public CacheManagerCustomizers cacheManagerCustomizers(
@ -83,14 +74,10 @@ public class CacheAutoConfiguration {
} }
@Bean @Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public CacheManagerValidator cacheAutoConfigurationValidator(
public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() { CacheProperties cacheProperties,
return new CacheManagerValidatorPostProcessor(); ObjectProvider<CacheManager> cacheManager) {
} return new CacheManagerValidator(cacheProperties, cacheManager);
@Bean(name = VALIDATOR_BEAN_NAME)
public CacheManagerValidator cacheAutoConfigurationValidator() {
return new CacheManagerValidator();
} }
@Configuration @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 * Bean used to validate that a CacheManager exists and provide a more meaningful
* exception. * exception.
*/ */
static class CacheManagerValidator { static class CacheManagerValidator implements InitializingBean {
@Autowired private final CacheProperties cacheProperties;
private CacheProperties cacheProperties;
@Autowired(required = false) private final ObjectProvider<CacheManager> cacheManager;
private CacheManager cacheManager;
@PostConstruct CacheManagerValidator(CacheProperties cacheProperties,
public void checkHasCacheManager() { ObjectProvider<CacheManager> cacheManager) {
Assert.notNull(this.cacheManager, this.cacheProperties = cacheProperties;
this.cacheManager = cacheManager;
}
@Override
public void afterPropertiesSet() {
Assert.notNull(this.cacheManager.getIfAvailable(),
"No cache manager could " "No cache manager could "
+ "be auto-configured, check your configuration (caching " + "be auto-configured, check your configuration (caching "
+ "type is '" + this.cacheProperties.getType() + "')"); + "type is '" + this.cacheProperties.getType() + "')");

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; 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.cache.support.MockCachingProvider;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration; import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.test.util.EnvironmentTestUtils;
@ -770,6 +772,17 @@ public class CacheAutoConfigurationTests {
validateCaffeineCacheWithStats(); 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() { private void validateCaffeineCacheWithStats() {
CaffeineCacheManager cacheManager = validateCacheManager( CaffeineCacheManager cacheManager = validateCacheManager(
CaffeineCacheManager.class); 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<CacheManager> cacheManagers = new ArrayList<CacheManager>();
@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;
}
}
} }