diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java index a849cf301f..8b3277f0d9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java @@ -203,13 +203,12 @@ class ValueObjectBinder implements DataObjectBinder { constructor = candidate; } } - return (constructor != null) ? new DefaultValueObject<>((Constructor) constructor) : null; + return get((Constructor) constructor); } private static boolean isCandidateConstructor(Constructor candidate, Predicate> filter) { int modifiers = candidate.getModifiers(); - return !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers) - && candidate.getParameterCount() > 0 && filter.test(candidate); + return !Modifier.isPrivate(modifiers) && !Modifier.isProtected(modifiers) && filter.test(candidate); } static DefaultValueObject get(Constructor constructor) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java index 5ab44292a6..86e9f46b1a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java @@ -807,6 +807,35 @@ class ConfigurationPropertiesTests { assertThat(bean.getNested().getAge()).isEqualTo(5); } + @Test // gh-18485 + void loadWhenBindingToMultiConstructorConfigurationProperties() { + MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + Map source = new HashMap<>(); + source.put("test.nested[0].name", "spring"); + source.put("test.nested[0].age", "5"); + sources.addLast(new MapPropertySource("test", source)); + load(MultiConstructorConfigurationPropertiesConfiguration.class); + MultiConstructorConfigurationListProperties bean = this.context + .getBean(MultiConstructorConfigurationListProperties.class); + MultiConstructorConfigurationProperties nested = bean.getNested().get(0); + assertThat(nested.getName()).isEqualTo("spring"); + assertThat(nested.getAge()).isEqualTo(5); + } + + @Test // gh-18485 + void loadWhenBindingToMultiConstructorConfigurationPropertiesUsingShortcutSyntax() { + MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + Map source = new HashMap<>(); + source.put("test.nested[0]", "spring"); + sources.addLast(new MapPropertySource("test", source)); + load(MultiConstructorConfigurationPropertiesConfiguration.class); + MultiConstructorConfigurationListProperties bean = this.context + .getBean(MultiConstructorConfigurationListProperties.class); + MultiConstructorConfigurationProperties nested = bean.getNested().get(0); + assertThat(nested.getName()).isEqualTo("spring"); + assertThat(nested.getAge()).isEqualTo(0); + } + private AnnotationConfigApplicationContext load(Class configuration, String... inlinedProperties) { return load(new Class[] { configuration }, inlinedProperties); } @@ -1968,4 +1997,21 @@ class ConfigurationPropertiesTests { } + @ConfigurationProperties("test") + static class MultiConstructorConfigurationListProperties { + + private List nested = new ArrayList<>(); + + List getNested() { + return this.nested; + } + + } + + @Configuration(proxyBeanMethods = false) + @EnableConfigurationProperties(MultiConstructorConfigurationListProperties.class) + static class MultiConstructorConfigurationPropertiesConfiguration { + + } + } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/MultiConstructorConfigurationProperties.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/MultiConstructorConfigurationProperties.java new file mode 100644 index 0000000000..c259d4249c --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/MultiConstructorConfigurationProperties.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2019 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.context.properties; + +/** + * Class used to test multi-constructor binding. Must be public and have public + * constructors. + * + * @author Phillip Webb + */ +public class MultiConstructorConfigurationProperties { + + private String name; + + private int age; + + public MultiConstructorConfigurationProperties() { + } + + public MultiConstructorConfigurationProperties(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return this.age; + } + + public void setAge(int age) { + this.age = age; + } + +}