diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 13a7b82487a..036766d2cea 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -177,6 +177,7 @@ class ConfigurationClassParser { String[] locations = (String[]) propertySourceAttributes.get("value"); ClassLoader classLoader = this.resourceLoader.getClassLoader(); for (String location : locations) { + location = this.environment.resolveRequiredPlaceholders(location); ResourcePropertySource ps = StringUtils.hasText(name) ? new ResourcePropertySource(name, location, classLoader) : new ResourcePropertySource(location, classLoader); diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/PropertySource.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/PropertySource.java index f1fb183f613..a928d9b672d 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/annotation/PropertySource.java +++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/PropertySource.java @@ -54,6 +54,32 @@ import java.lang.annotation.Target; * configuration class and then used when populating the {@code TestBean} object. Given * the configuration above, a call to {@code testBean.getName()} will return "myTestBean". * + *
+ * @Configuration
+ * @PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
+ * public class AppConfig {
+ * @Autowired
+ * Environment env;
+ *
+ * @Bean
+ * public TestBean testBean() {
+ * TestBean testBean = new TestBean();
+ * testBean.setName(env.getProperty("testbean.name"));
+ * return testBean;
+ * }
+ * }
+ *
+ * Assuming that "my.placeholder" is present in one of the property sources already
+ * registered, e.g. system properties or environment variables, the placeholder will
+ * be resolved to the corresponding value. If not, then "default/path" will be used as a
+ * default. Expressing a default value (delimited by colon ":") is optional. If no
+ * default is specified and a property cannot be resolved, an {@code
+ * IllegalArgumentException} will be thrown.
+ *
* Resource location wildcards (e.g. **/*.properties) are not permitted; each + * location must evaluate to exactly one {@code .properties} resource. + *
${...} placeholders will be resolved against any/all property sources already + * registered with the {@code Environment}. See {@linkplain PropertySource above} for + * examples. + *
Each location will be added to the enclosing {@code Environment} as its own + * property source, and in the order declared. */ String[] value(); diff --git a/org.springframework.context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java b/org.springframework.context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java index 5efa03d3f59..133b9639670 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/annotation/PropertySourceAnnotationTests.java @@ -93,6 +93,62 @@ public class PropertySourceAnnotationTests { } } + @Test(expected=IllegalArgumentException.class) + public void withUnresolvablePlaceholder() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ConfigWithUnresolvablePlaceholder.class); + ctx.refresh(); + } + + @Test + public void withUnresolvablePlaceholderAndDefault() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ConfigWithUnresolvablePlaceholderAndDefault.class); + ctx.refresh(); + assertThat(ctx.getBean(TestBean.class).getName(), equalTo("p1TestBean")); + } + + @Test + public void withResolvablePlaceholder() { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(ConfigWithResolvablePlaceholder.class); + System.setProperty("path.to.properties", "org/springframework/context/annotation"); + ctx.refresh(); + assertThat(ctx.getBean(TestBean.class).getName(), equalTo("p1TestBean")); + System.clearProperty("path.to.properties"); + } + + + @Configuration + @PropertySource(value="classpath:${unresolvable}/p1.properties") + static class ConfigWithUnresolvablePlaceholder { + } + + + @Configuration + @PropertySource(value="classpath:${unresolvable:org/springframework/context/annotation}/p1.properties") + static class ConfigWithUnresolvablePlaceholderAndDefault { + @Inject Environment env; + + @Bean + public TestBean testBean() { + return new TestBean(env.getProperty("testbean.name")); + } + } + + + @Configuration + @PropertySource(value="classpath:${path.to.properties}/p1.properties") + static class ConfigWithResolvablePlaceholder { + @Inject Environment env; + + @Bean + public TestBean testBean() { + return new TestBean(env.getProperty("testbean.name")); + } + } + + @Configuration @PropertySource(name="p1", value="classpath:org/springframework/context/annotation/p1.properties")