Consider relaxed variants of target name when filtering property names
Previously, when ignoreUnknownFields was false and property names were being filtered based on whether or not they begin with the target name, relaxed variants of the target name were not considered. This resulted in different delimiters resulting in a non-match. For example, the property ENV_FOO_NAME would be filtered out when the target name was env.foo. This commit updates PropertiesConfigurationFactory to pass all of the relaxed variants for the target name to the matcher. For the example above one of those variants will be env_foo which matches ENV_FOO_NAME due to the matching delimiter. PropertiesConfigurationFactory was already creating a RelaxedNames instance for the target name. The code has been reworked a little to allow these relaxed names to be reused, thereby avoiding the cost of computing all of the relaxed variants of the target name a second time. Closes gh-4775
This commit is contained in:
parent
5a7dece144
commit
e203a689bf
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.bind;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
|
@ -262,19 +263,23 @@ public class PropertiesConfigurationFactory<T>
|
|||
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
|
||||
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
|
||||
customizeBinder(dataBinder);
|
||||
Set<String> names = getNames();
|
||||
PropertyValues propertyValues = getPropertyValues(names);
|
||||
Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
|
||||
Set<String> names = getNames(relaxedTargetNames);
|
||||
PropertyValues propertyValues = getPropertyValues(names, relaxedTargetNames);
|
||||
dataBinder.bind(propertyValues);
|
||||
if (this.validator != null) {
|
||||
validate(dataBinder);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getNames() {
|
||||
private Iterable<String> getRelaxedTargetNames() {
|
||||
return (this.target != null && StringUtils.hasLength(this.targetName)
|
||||
? new RelaxedNames(this.targetName) : null);
|
||||
}
|
||||
|
||||
private Set<String> getNames(Iterable<String> prefixes) {
|
||||
Set<String> names = new LinkedHashSet<String>();
|
||||
if (this.target != null) {
|
||||
Iterable<String> prefixes = (StringUtils.hasLength(this.targetName)
|
||||
? new RelaxedNames(this.targetName) : null);
|
||||
PropertyDescriptor[] descriptors = BeanUtils
|
||||
.getPropertyDescriptors(this.target.getClass());
|
||||
for (PropertyDescriptor descriptor : descriptors) {
|
||||
|
|
@ -300,31 +305,38 @@ public class PropertiesConfigurationFactory<T>
|
|||
return names;
|
||||
}
|
||||
|
||||
private PropertyValues getPropertyValues(Set<String> names) {
|
||||
private PropertyValues getPropertyValues(Set<String> names,
|
||||
Iterable<String> relaxedTargetNames) {
|
||||
if (this.properties != null) {
|
||||
return new MutablePropertyValues(this.properties);
|
||||
}
|
||||
return getPropertySourcesPropertyValues(names);
|
||||
return getPropertySourcesPropertyValues(names, relaxedTargetNames);
|
||||
}
|
||||
|
||||
private PropertyValues getPropertySourcesPropertyValues(Set<String> names) {
|
||||
PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names);
|
||||
private PropertyValues getPropertySourcesPropertyValues(Set<String> names,
|
||||
Iterable<String> relaxedTargetNames) {
|
||||
PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names,
|
||||
relaxedTargetNames);
|
||||
return new PropertySourcesPropertyValues(this.propertySources, names, includes);
|
||||
}
|
||||
|
||||
private PropertyNamePatternsMatcher getPropertyNamePatternsMatcher(
|
||||
Set<String> names) {
|
||||
private PropertyNamePatternsMatcher getPropertyNamePatternsMatcher(Set<String> names,
|
||||
Iterable<String> relaxedTargetNames) {
|
||||
if (this.ignoreUnknownFields && !isMapTarget()) {
|
||||
// Since unknown fields are ignored we can filter them out early to save
|
||||
// unnecessary calls to the PropertySource.
|
||||
return new DefaultPropertyNamePatternsMatcher(EXACT_DELIMITERS, true, names);
|
||||
}
|
||||
if (this.targetName != null) {
|
||||
if (relaxedTargetNames != null) {
|
||||
// We can filter properties to those starting with the target name, but
|
||||
// we can't do a complete filter since we need to trigger the
|
||||
// unknown fields check
|
||||
Set<String> relaxedNames = new HashSet<String>();
|
||||
for (String relaxedTargetName : relaxedTargetNames) {
|
||||
relaxedNames.add(relaxedTargetName);
|
||||
}
|
||||
return new DefaultPropertyNamePatternsMatcher(TARGET_NAME_DELIMITERS, true,
|
||||
this.targetName);
|
||||
relaxedNames);
|
||||
}
|
||||
// Not ideal, we basically can't filter anything
|
||||
return PropertyNamePatternsMatcher.ALL;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,36 @@ public class PropertiesConfigurationFactoryTests {
|
|||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindWithDelimitedPrefixUsingMatchingDelimiter() throws Exception {
|
||||
this.targetName = "env_foo";
|
||||
this.ignoreUnknownFields = false;
|
||||
MutablePropertySources propertySources = new MutablePropertySources();
|
||||
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment",
|
||||
Collections.<String, Object>singletonMap("ENV_FOO_NAME", "blah")));
|
||||
propertySources.addLast(new RandomValuePropertySource("random"));
|
||||
setupFactory();
|
||||
this.factory.setPropertySources(propertySources);
|
||||
this.factory.afterPropertiesSet();
|
||||
Foo foo = this.factory.getObject();
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindWithDelimitedPrefixUsingDifferentDelimiter() throws Exception {
|
||||
this.targetName = "env.foo";
|
||||
MutablePropertySources propertySources = new MutablePropertySources();
|
||||
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment",
|
||||
Collections.<String, Object>singletonMap("ENV_FOO_NAME", "blah")));
|
||||
propertySources.addLast(new RandomValuePropertySource("random"));
|
||||
this.ignoreUnknownFields = false;
|
||||
setupFactory();
|
||||
this.factory.setPropertySources(propertySources);
|
||||
this.factory.afterPropertiesSet();
|
||||
Foo foo = this.factory.getObject();
|
||||
assertEquals("blah", foo.name);
|
||||
}
|
||||
|
||||
private Foo createFoo(final String values) throws Exception {
|
||||
setupFactory();
|
||||
return bindFoo(values);
|
||||
|
|
|
|||
Loading…
Reference in New Issue