Resolve ${...} placeholders in @PropertySource

Issue: SPR-8442

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4516 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Chris Beams 2011-06-13 04:23:08 +00:00
parent 419288562b
commit cf8533fb97
3 changed files with 91 additions and 5 deletions

View File

@ -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);

View File

@ -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".
*
* <h3>Resolving placeholders within @PropertySource resource locations</h3>
* 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:
* <pre class="code">
* &#064;Configuration
* &#064;PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
* public class AppConfig {
* &#064;Autowired
* Environment env;
*
* &#064;Bean
* public TestBean testBean() {
* TestBean testBean = new TestBean();
* testBean.setName(env.getProperty("testbean.name"));
* return testBean;
* }
* }</pre>
*
* 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.
*
* <h3>A note on property overriding with @PropertySource</h3>
* 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"}.
* <p>Resource location wildcards (e.g. *&#42;/*.properties) are not permitted; each
* location must evaluate to exactly one {@code .properties} resource.
* <p>${...} placeholders will be resolved against any/all property sources already
* registered with the {@code Environment}. See {@linkplain PropertySource above} for
* examples.
* <p>Each location will be added to the enclosing {@code Environment} as its own
* property source, and in the order declared.
*/
String[] value();

View File

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