Ignore unbound collection properties if collection bound
As of Spring Boot 2.0, if Collection properties are specified in multiple property sources, only the elements from the property source with the highest precedence are used for binding. This caused an `UnboundConfigurationPropertiesException` if the size of the collection from the higher order property source was smaller and `ignoreUnknownFields` was set to true. This commit ignores unbound collection properties if the collection was properly bound. Fixes gh-16290
This commit is contained in:
parent
0c2e71cd08
commit
91c1fc3d97
|
|
@ -106,7 +106,23 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler {
|
|||
|
||||
private boolean isUnbound(ConfigurationPropertyName name,
|
||||
ConfigurationPropertyName candidate) {
|
||||
return name.isAncestorOf(candidate) && !this.boundNames.contains(candidate);
|
||||
if (name.isAncestorOf(candidate)) {
|
||||
if (!this.boundNames.contains(candidate)
|
||||
&& !isOverriddenCollectionElement(candidate)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isOverriddenCollectionElement(ConfigurationPropertyName candidate) {
|
||||
int length = candidate.getNumberOfElements();
|
||||
if (candidate.isNumericIndex(length - 1)) {
|
||||
ConfigurationPropertyName propertyName = candidate
|
||||
.chop(candidate.getNumberOfElements() - 1);
|
||||
return this.boundNames.contains(propertyName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,35 @@ public class NoUnboundElementsBindHandlerTests {
|
|||
assertThat(bound.getFoo()).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindWhenUsingNoUnboundElementsHandlerShouldBindIfUnboundCollectionProperties() {
|
||||
MockConfigurationPropertySource source1 = new MockConfigurationPropertySource();
|
||||
source1.put("example.foo[0]", "bar");
|
||||
MockConfigurationPropertySource source2 = new MockConfigurationPropertySource();
|
||||
source2.put("example.foo[0]", "bar");
|
||||
source2.put("example.foo[1]", "baz");
|
||||
this.sources.add(source1);
|
||||
this.sources.add(source2);
|
||||
this.binder = new Binder(this.sources);
|
||||
NoUnboundElementsBindHandler handler = new NoUnboundElementsBindHandler();
|
||||
ExampleWithList bound = this.binder
|
||||
.bind("example", Bindable.of(ExampleWithList.class), handler).get();
|
||||
assertThat(bound.getFoo()).containsExactly("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindWhenUsingNoUnboundElementsHandlerAndUnboundListElementsShouldThrowException() {
|
||||
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
|
||||
source.put("example.foo[0]", "bar");
|
||||
this.sources.add(source);
|
||||
this.binder = new Binder(this.sources);
|
||||
assertThatExceptionOfType(BindException.class)
|
||||
.isThrownBy(() -> this.binder.bind("example", Bindable.of(Example.class),
|
||||
new NoUnboundElementsBindHandler()))
|
||||
.satisfies((ex) -> assertThat(ex.getCause().getMessage())
|
||||
.contains("The elements [example.foo[0]] were left unbound"));
|
||||
}
|
||||
|
||||
public static class Example {
|
||||
|
||||
private String foo;
|
||||
|
|
@ -122,4 +151,18 @@ public class NoUnboundElementsBindHandlerTests {
|
|||
|
||||
}
|
||||
|
||||
public static class ExampleWithList {
|
||||
|
||||
private List<String> foo;
|
||||
|
||||
public List<String> getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
public void setFoo(List<String> foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue