From cf8533fb977ed52585147c15587ed478d85b3aca Mon Sep 17 00:00:00 2001 From: Chris Beams Date: Mon, 13 Jun 2011 04:23:08 +0000 Subject: [PATCH] Resolve ${...} placeholders in @PropertySource Issue: SPR-8442 git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4516 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../annotation/ConfigurationClassParser.java | 1 + .../context/annotation/PropertySource.java | 39 +++++++++++-- .../PropertySourceAnnotationTests.java | 56 +++++++++++++++++++ 3 files changed, 91 insertions(+), 5 deletions(-) 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". * + *

Resolving placeholders within @PropertySource resource locations

+ * Any ${...} placeholders present in a {@code @PropertySource} {@linkplain #value() + * resource location} will be resolved against the set of property sources already + * registered against the environment. For example: + *
+ * @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. + * *

A note on property overriding with @PropertySource

* In cases where a given property key exists in more than one {@code .properties} * file, the last {@code @PropertySource} annotation processed will 'win' and override. @@ -119,11 +145,14 @@ public @interface PropertySource { /** * Indicate the resource location(s) of the properties file to be loaded. * For example, {@code "classpath:/com/myco/app.properties"} or - * {@code "file:/path/to/file"}. Note that resource location wildcards - * are not permitted, and that each location must evaluate to exactly one - * {@code .properties} resource. Each location will be added to the - * enclosing {@code Environment} as its own property source, and in the order - * declared. + * {@code "file:/path/to/file"}. + *

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")