diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java index dd4876b8d6..b92460dc69 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java @@ -18,6 +18,7 @@ package org.springframework.boot.context.properties; import java.io.IOException; import java.util.Iterator; +import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; @@ -203,15 +204,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc } private PropertySources deducePropertySources() { - try { - PropertySourcesPlaceholderConfigurer configurer = this.beanFactory - .getBean(PropertySourcesPlaceholderConfigurer.class); - PropertySources propertySources = configurer.getAppliedPropertySources(); + PropertySourcesPlaceholderConfigurer configurer = getSinglePropertySourcesPlaceholderConfigurer(); + if (configurer != null) { // Flatten the sources into a single list so they can be iterated - return new FlatPropertySources(propertySources); - } - catch (NoSuchBeanDefinitionException ex) { - // Continue if no PropertySourcesPlaceholderConfigurer bean + return new FlatPropertySources(configurer.getAppliedPropertySources()); } if (this.environment instanceof ConfigurableEnvironment) { @@ -224,6 +220,20 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc return new MutablePropertySources(); } + private PropertySourcesPlaceholderConfigurer getSinglePropertySourcesPlaceholderConfigurer() { + // Take care not to cause early instantiation of all FactoryBeans + if (this.beanFactory instanceof ListableBeanFactory) { + ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory; + Map beans = listableBeanFactory + .getBeansOfType(PropertySourcesPlaceholderConfigurer.class, false, + false); + if (beans.size() == 1) { + return beans.values().iterator().next(); + } + } + return null; + } + private T getOptionalBean(String name, Class type) { try { return this.beanFactory.getBean(name, type); diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java index 58367b127f..9ef038defe 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessorTests.java @@ -21,8 +21,13 @@ import javax.validation.constraints.NotNull; import org.junit.After; import org.junit.Test; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.boot.test.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -36,8 +41,10 @@ import org.springframework.validation.Validator; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -161,6 +168,26 @@ public class ConfigurationPropertiesBindingPostProcessorTests { .getFoo(), equalTo("${fooValue}")); } + @Test + public void configurationPropertiesWithFactoryBean() throws Exception { + ConfigurationPropertiesWithFactoryBean.factoryBeanInit = false; + this.context = new AnnotationConfigApplicationContext() { + @Override + protected void onRefresh() throws BeansException { + assertFalse("Init too early", + ConfigurationPropertiesWithFactoryBean.factoryBeanInit); + super.onRefresh(); + } + }; + this.context.register(ConfigurationPropertiesWithFactoryBean.class); + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(FactoryBeanTester.class); + beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); + this.context.registerBeanDefinition("test", beanDefinition); + this.context.refresh(); + assertTrue("No init", ConfigurationPropertiesWithFactoryBean.factoryBeanInit); + } + @Configuration @EnableConfigurationProperties public static class TestConfigurationWithValidatingSetter { @@ -351,4 +378,38 @@ public class ConfigurationPropertiesBindingPostProcessorTests { } + @Configuration + @EnableConfigurationProperties + public static class ConfigurationPropertiesWithFactoryBean { + + public static boolean factoryBeanInit; + + } + + @SuppressWarnings("rawtypes") + // Must be a raw type + static class FactoryBeanTester implements FactoryBean, InitializingBean { + + @Override + public Object getObject() throws Exception { + return Object.class; + } + + @Override + public Class getObjectType() { + return null; + } + + @Override + public boolean isSingleton() { + return true; + } + + @Override + public void afterPropertiesSet() throws Exception { + ConfigurationPropertiesWithFactoryBean.factoryBeanInit = true; + } + + } + }