提交 06e34f05 编写于 作者: C Chris Beams

Allow PropertyResolvers to ignore unresolvable ${placeholders}

Prior to this commit, the PropertyResolver API (and therefore the
Environment API) allowed callers a choice between
 #resolvePlaceholders and #resolveRequiredPlaceholders for low-level
${placeholder} resolution. However, when calling the higher level
 #getProperty variants, users had no control over whether property
values returned with unresolvable ${placeholders} would result in an
exception or simply be passed through.

This commit introduces a #setIgnoreUnresolvableNestedPlaceholders
property via ConfigurablePropertyResolver, defaulting to false, the
value of which is respected by AbstractPropertyResolver#getProperty
method implementations. See the new test in
PropertySourcesPropertyResolverTests for usage examples.

Issue: SPR-9569, SPR-9473
上级 01272fb0
......@@ -464,6 +464,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
return this.propertyResolver.resolveRequiredPlaceholders(text);
}
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
this.propertyResolver.setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvableNestedPlaceholders);
}
public void setConversionService(ConfigurableConversionService conversionService) {
this.propertyResolver.setConversionService(conversionService);
}
......
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
......@@ -45,6 +45,7 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
private PropertyPlaceholderHelper nonStrictHelper;
private PropertyPlaceholderHelper strictHelper;
private boolean ignoreUnresolvableNestedPlaceholders = false;
private String placeholderPrefix = PLACEHOLDER_PREFIX;
private String placeholderSuffix = PLACEHOLDER_SUFFIX;
......@@ -142,6 +143,28 @@ public abstract class AbstractPropertyResolver implements ConfigurablePropertyRe
return doResolvePlaceholders(text, strictHelper);
}
/**
* {@inheritDoc}
* <p>The default value for this implementation is {@code false}.
* @since 3.2
*/
public void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders) {
this.ignoreUnresolvableNestedPlaceholders = ignoreUnresolvableNestedPlaceholders;
}
/**
* Resolve placeholders within the given string, deferring to the value of
* {@link #setIgnoreUnresolvableNestedPlaceholders(boolean)} to determine whether any
* unresolvable placeholders should raise an exception or be ignored.
* @since 3.2
* @see #setIgnoreUnresolvableNestedPlaceholders(boolean)
*/
protected String resolveNestedPlaceholders(String value) {
return this.ignoreUnresolvableNestedPlaceholders ?
this.resolvePlaceholders(value) :
this.resolveRequiredPlaceholders(value);
}
private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
this.valueSeparator, ignoreUnresolvablePlaceholders);
......
/*
* Copyright 2002-2011 the original author or authors.
* Copyright 2002-2012 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.
......@@ -87,4 +87,17 @@ public interface ConfigurablePropertyResolver extends PropertyResolver {
* properties are not resolvable.
*/
void validateRequiredProperties() throws MissingRequiredPropertiesException;
/**
* Set whether to throw an exception when encountering an unresolvable placeholder
* nested within the value of a given property. A {@code false} value indicates strict
* resolution, i.e. that an exception will be thrown. A {@code true} value indicates
* that unresolvable nested placeholders should be passed through in their unresolved
* ${...} form.
* <p>Implementations of {@link #getProperty(String)} and its variants must inspect
* the value set here to determine correct behavior when property values contain
* unresolvable placeholders.
* @since 3.2
*/
void setIgnoreUnresolvableNestedPlaceholders(boolean ignoreUnresolvableNestedPlaceholders);
}
......@@ -73,7 +73,7 @@ public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
if ((value = propertySource.getProperty(key)) != null) {
Class<?> valueType = value.getClass();
if (String.class.equals(valueType)) {
value = this.resolveRequiredPlaceholders((String) value);
value = this.resolveNestedPlaceholders((String) value);
}
if (debugEnabled) {
logger.debug(
......
......@@ -20,8 +20,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Test;
......@@ -364,7 +362,7 @@ public class PropertySourcesPropertyResolverTests {
.withProperty("pL", "${pR}") // cyclic reference left
.withProperty("pR", "${pL}") // cyclic reference right
);
PropertySourcesPropertyResolver pr = new PropertySourcesPropertyResolver(ps);
ConfigurablePropertyResolver pr = new PropertySourcesPropertyResolver(ps);
assertThat(pr.getProperty("p1"), equalTo("v1"));
assertThat(pr.getProperty("p2"), equalTo("v2"));
assertThat(pr.getProperty("p3"), equalTo("v1:v2"));
......@@ -383,6 +381,45 @@ public class PropertySourcesPropertyResolverTests {
}
}
@Test
public void ignoreUnresolvableNestedPlaceholdersIsConfigurable() {
MutablePropertySources ps = new MutablePropertySources();
ps.addFirst(new MockPropertySource()
.withProperty("p1", "v1")
.withProperty("p2", "v2")
.withProperty("p3", "${p1}:${p2}:${bogus:def}") // unresolvable w/ default
.withProperty("p4", "${p1}:${p2}:${bogus}") // unresolvable placeholder
);
ConfigurablePropertyResolver pr = new PropertySourcesPropertyResolver(ps);
assertThat(pr.getProperty("p1"), equalTo("v1"));
assertThat(pr.getProperty("p2"), equalTo("v2"));
assertThat(pr.getProperty("p3"), equalTo("v1:v2:def"));
// placeholders nested within the value of "p4" are unresolvable and cause an
// exception by default
try {
pr.getProperty("p4");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString(
"Could not resolve placeholder 'bogus' in string value [${p1}:${p2}:${bogus}]"));
}
// relax the treatment of unresolvable nested placeholders
pr.setIgnoreUnresolvableNestedPlaceholders(true);
// and observe they now pass through unresolved
assertThat(pr.getProperty("p4"), equalTo("v1:v2:${bogus}"));
// resolve[Nested]Placeholders methods behave as usual regardless the value of
// ignoreUnresolvableNestedPlaceholders
assertThat(pr.resolvePlaceholders("${p1}:${p2}:${bogus}"), equalTo("v1:v2:${bogus}"));
try {
pr.resolveRequiredPlaceholders("${p1}:${p2}:${bogus}");
} catch (IllegalArgumentException ex) {
assertThat(ex.getMessage(), containsString(
"Could not resolve placeholder 'bogus' in string value [${p1}:${p2}:${bogus}]"));
}
}
static interface SomeType { }
static class SpecificType implements SomeType { }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册