diff --git a/spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java b/spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java index a0907a1f3a1..6b5824cea5e 100644 --- a/spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java +++ b/spring-context/src/main/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -24,10 +24,12 @@ import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurablePropertyResolver; import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertyResolver; import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySources; import org.springframework.core.env.PropertySourcesPropertyResolver; @@ -57,6 +59,7 @@ import org.springframework.util.StringValueResolver; * * @author Chris Beams * @author Juergen Hoeller + * @author Sam Brannen * @since 3.1 * @see org.springframework.core.env.ConfigurableEnvironment * @see org.springframework.beans.factory.config.PlaceholderConfigurerSupport @@ -129,12 +132,25 @@ public class PropertySourcesPlaceholderConfigurer extends PlaceholderConfigurerS if (this.propertySources == null) { this.propertySources = new MutablePropertySources(); if (this.environment != null) { + PropertyResolver propertyResolver = this.environment; + // If the ignoreUnresolvablePlaceholders flag is set to true, we have to create a + // local PropertyResolver to enforce that setting, since the Environment is most + // likely not configured with ignoreUnresolvablePlaceholders set to true. + // See https://github.com/spring-projects/spring-framework/issues/27947 + if (this.ignoreUnresolvablePlaceholders && (this.environment instanceof ConfigurableEnvironment)) { + ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) this.environment; + PropertySourcesPropertyResolver resolver = + new PropertySourcesPropertyResolver(configurableEnvironment.getPropertySources()); + resolver.setIgnoreUnresolvableNestedPlaceholders(true); + propertyResolver = resolver; + } + PropertyResolver propertyResolverToUse = propertyResolver; this.propertySources.addLast( new PropertySource(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) { @Override @Nullable public String getProperty(String key) { - return this.source.getProperty(key); + return propertyResolverToUse.getProperty(key); } } ); diff --git a/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java b/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java index bdb41244bdd..42fe1a715e2 100644 --- a/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/PropertySourcesPlaceholderConfigurerTests.java @@ -21,9 +21,14 @@ import java.util.Properties; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; @@ -40,8 +45,11 @@ import static org.springframework.beans.factory.support.BeanDefinitionBuilder.ge import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; /** + * Tests for {@link PropertySourcesPlaceholderConfigurer}. + * * @author Chris Beams * @author Juergen Hoeller + * @author Sam Brannen * @since 3.1 */ public class PropertySourcesPlaceholderConfigurerTests { @@ -159,8 +167,11 @@ public class PropertySourcesPlaceholderConfigurerTests { PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer(); //pc.setIgnoreUnresolvablePlaceholders(false); // the default - assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() -> - ppc.postProcessBeanFactory(bf)); + assertThatExceptionOfType(BeanDefinitionStoreException.class) + .isThrownBy(() -> ppc.postProcessBeanFactory(bf)) + .havingCause() + .isExactlyInstanceOf(IllegalArgumentException.class) + .withMessage("Could not resolve placeholder 'my.name' in value \"${my.name}\""); } @Test @@ -177,6 +188,38 @@ public class PropertySourcesPlaceholderConfigurerTests { assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("${my.name}"); } + @Test + // https://github.com/spring-projects/spring-framework/issues/27947 + public void ignoreUnresolvablePlaceholdersInAtValueAnnotation__falseIsDefault() { + MockPropertySource mockPropertySource = new MockPropertySource("test"); + mockPropertySource.setProperty("my.key", "${enigma}"); + @SuppressWarnings("resource") + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.getEnvironment().getPropertySources().addLast(mockPropertySource); + context.register(IgnoreUnresolvablePlaceholdersFalseConfig.class); + + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(context::refresh) + .havingCause() + .isExactlyInstanceOf(IllegalArgumentException.class) + .withMessage("Could not resolve placeholder 'enigma' in value \"${enigma}\""); + } + + @Test + // https://github.com/spring-projects/spring-framework/issues/27947 + public void ignoreUnresolvablePlaceholdersInAtValueAnnotation_true() { + MockPropertySource mockPropertySource = new MockPropertySource("test"); + mockPropertySource.setProperty("my.key", "${enigma}"); + @SuppressWarnings("resource") + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.getEnvironment().getPropertySources().addLast(mockPropertySource); + context.register(IgnoreUnresolvablePlaceholdersTrueConfig.class); + context.refresh(); + + IgnoreUnresolvablePlaceholdersTrueConfig config = context.getBean(IgnoreUnresolvablePlaceholdersTrueConfig.class); + assertThat(config.value).isEqualTo("${enigma}"); + } + @Test @SuppressWarnings("serial") public void nestedUnresolvablePlaceholder() { @@ -402,4 +445,30 @@ public class PropertySourcesPlaceholderConfigurerTests { } } + @Configuration + static class IgnoreUnresolvablePlaceholdersFalseConfig { + + @Value("${my.key}") + String value; + + @Bean + static PropertySourcesPlaceholderConfigurer pspc() { + return new PropertySourcesPlaceholderConfigurer(); + } + } + + @Configuration + static class IgnoreUnresolvablePlaceholdersTrueConfig { + + @Value("${my.key}") + String value; + + @Bean + static PropertySourcesPlaceholderConfigurer pspc() { + PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); + pspc.setIgnoreUnresolvablePlaceholders(true); + return pspc; + } + } + }