diff --git a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java index c8f61267519..da4bae7c8e5 100644 --- a/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java +++ b/spring-boot/src/main/java/org/springframework/boot/bind/RelaxedDataBinder.java @@ -206,14 +206,18 @@ public class RelaxedDataBinder extends DataBinder { MutablePropertyValues rtn = new MutablePropertyValues(); for (PropertyValue value : propertyValues.getPropertyValues()) { String name = value.getName(); - for (String candidate : new RelaxedNames(this.namePrefix)) { - if (name.startsWith(candidate)) { - name = name.substring(candidate.length()); - if (!(this.ignoreNestedProperties && name.contains("."))) { - PropertyOrigin propertyOrigin = OriginCapablePropertyValue - .getOrigin(value); - rtn.addPropertyValue(new OriginCapablePropertyValue(name, value - .getValue(), propertyOrigin)); + for (String prefix : new RelaxedNames(stripLastDot(this.namePrefix))) { + for (String separator : new String[] { ".", "_" }) { + String candidate = (StringUtils.hasLength(prefix) ? prefix + + separator : prefix); + if (name.startsWith(candidate)) { + name = name.substring(candidate.length()); + if (!(this.ignoreNestedProperties && name.contains("."))) { + PropertyOrigin propertyOrigin = OriginCapablePropertyValue + .getOrigin(value); + rtn.addPropertyValue(new OriginCapablePropertyValue(name, + value.getValue(), propertyOrigin)); + } } } } @@ -221,6 +225,13 @@ public class RelaxedDataBinder extends DataBinder { return rtn; } + private String stripLastDot(String string) { + if (StringUtils.hasLength(string) && string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + return string; + } + private PropertyValue modifyProperty(BeanWrapper target, PropertyValue propertyValue) { String name = propertyValue.getName(); String normalizedName = normalizePath(target, name); diff --git a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java index 1c182a9ebe9..aefc48d153d 100644 --- a/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/bind/PropertiesConfigurationFactoryTests.java @@ -17,15 +17,18 @@ package org.springframework.boot.bind; import java.io.IOException; +import java.util.Collections; import javax.validation.Validation; import javax.validation.constraints.NotNull; import org.junit.Test; import org.springframework.beans.NotWritablePropertyException; +import org.springframework.boot.context.config.RandomValuePropertySource; import org.springframework.context.support.StaticMessageSource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.env.SystemEnvironmentPropertySource; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.mock.env.MockPropertySource; @@ -115,6 +118,21 @@ public class PropertiesConfigurationFactoryTests { this.factory.afterPropertiesSet(); } + @Test + public void testBindWithDashPrefix() throws Exception { + // gh-4045 + this.targetName = "foo-bar"; + MutablePropertySources propertySources = new MutablePropertySources(); + propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", + Collections.singletonMap("FOO_BAR_NAME", "blah"))); + propertySources.addLast(new RandomValuePropertySource("random")); + 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);