From dec6ee20d45a1e0fb5f2b1ddd92568dabc41917e Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Fri, 25 Oct 2019 12:45:38 -0700 Subject: [PATCH] Fix error message when constructor binding fails to create bean The configuration property is always cleared before bean instantiation because the problem with bean instantiation might not necessarily be the last bound property. Fixes gh-18704 --- .../boot/context/properties/bind/Binder.java | 2 +- .../properties/bind/ValueObjectBinder.java | 1 + .../bind/ValueObjectBinderTests.java | 38 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java index 555efafc20b..61c68169646 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java @@ -551,7 +551,7 @@ public class Binder { this.configurationProperty = configurationProperty; } - private void clearConfigurationProperty() { + void clearConfigurationProperty() { this.configurationProperty = null; } 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 8b3277f0d9a..833afdfa1f8 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 @@ -61,6 +61,7 @@ class ValueObjectBinder implements DataObjectBinder { arg = (arg != null) ? arg : parameter.getDefaultValue(context.getConverter()); args.add(arg); } + context.clearConfigurationProperty(); return bound ? valueObject.instantiate(args) : null; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java index 48aa5dfabe7..2eb46837a0b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import com.atomikos.util.Assert; import org.junit.jupiter.api.Test; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; @@ -29,6 +30,7 @@ import org.springframework.boot.context.properties.source.MockConfigurationPrope import org.springframework.format.annotation.DateTimeFormat; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link ValueObjectBinder}. @@ -220,6 +222,20 @@ class ValueObjectBinderTests { assertThat(bean.getDate().toString()).isEqualTo("2019-05-10"); } + @Test + void bindWhenAllPropertiesBoundShouldClearConfigurationProperty() { // gh-18704 + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.bar", "hello"); + this.sources.add(source); + Bindable target = Bindable.of(ValidatingConstructorBean.class); + assertThatExceptionOfType(BindException.class).isThrownBy(() -> this.binder.bind("foo", target)) + .satisfies(this::noConfigurationProperty); + } + + private void noConfigurationProperty(BindException ex) { + assertThat(ex.getProperty()).isNull(); + } + static class ExampleValueBean { private final int intValue; @@ -413,4 +429,26 @@ class ValueObjectBinderTests { } + static class ValidatingConstructorBean { + + private final String foo; + + private final String bar; + + ValidatingConstructorBean(String foo, String bar) { + Assert.notNull("Foo must not be null", foo); + this.foo = foo; + this.bar = bar; + } + + String getFoo() { + return this.foo; + } + + String getBar() { + return this.bar; + } + + } + }