diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java index 8f765be23dd..9797d8dc4f1 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java @@ -411,7 +411,8 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc handler = new IgnoreErrorsBindHandler(handler); } if (!annotation.ignoreUnknownFields()) { - handler = new NoUnboundElementsBindHandler(handler); + handler = new NoUnboundElementsBindHandler(handler, + annotation.ignoreNestedProperties()); } if (annotation.ignoreNestedProperties()) { handler = new IgnoreNestedPropertiesBindHandler(handler); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java index 15ecfb8a8be..6014150d92c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandler.java @@ -40,14 +40,28 @@ import org.springframework.boot.context.properties.source.IterableConfigurationP */ public class NoUnboundElementsBindHandler extends AbstractBindHandler { + private final boolean ignoreNested; + private final Set boundNames = new HashSet<>(); public NoUnboundElementsBindHandler() { super(); + this.ignoreNested = false; + } + + public NoUnboundElementsBindHandler(boolean ignoreNested) { + super(); + this.ignoreNested = ignoreNested; } public NoUnboundElementsBindHandler(BindHandler parent) { super(parent); + this.ignoreNested = false; + } + + public NoUnboundElementsBindHandler(BindHandler parent, boolean ignoreNested) { + super(parent); + this.ignoreNested = ignoreNested; } @Override @@ -96,7 +110,11 @@ public class NoUnboundElementsBindHandler extends AbstractBindHandler { private boolean isUnbound(ConfigurationPropertyName name, ConfigurationPropertyName candidate) { - return name.isAncestorOf(candidate) && !this.boundNames.contains(candidate); + boolean isParent = candidate.getParent() != null + && name.equals(candidate.getParent()); + boolean isAncestor = name.isAncestorOf(candidate); + return ((this.ignoreNested ? isParent : isAncestor) + && !this.boundNames.contains(candidate)); } } diff --git a/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandlerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandlerTests.java index 0733005839d..0f70ce5eaf6 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandlerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/handler/NoUnboundElementsBindHandlerTests.java @@ -103,6 +103,37 @@ public class NoUnboundElementsBindHandlerTests { assertThat(bound.getFoo()).isEqualTo("bar"); } + @Test + public void bindWhenUsingNoUnboundElementsHandlerIgnoreNestedAndUnboundChildShouldThrowException() + throws Exception { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("example.foo", "bar"); + source.put("example.baz", "bar"); + this.sources.add(source); + this.binder = new Binder(this.sources); + try { + this.binder.bind("example", Bindable.of(Example.class), + new NoUnboundElementsBindHandler(true)); + fail("did not throw"); + } + catch (BindException ex) { + assertThat(ex.getCause().getMessage()) + .contains("The elements [example.baz] were left unbound"); + } + } + + @Test + public void bindWhenUsingNoUnboundElementsHandlerIgnoreNestedAndUnboundGrandchildShouldBind() + throws Exception { + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("example.foo", "bar"); + source.put("example.foo.baz", "bar"); + this.sources.add(source); + this.binder = new Binder(this.sources); + this.binder.bind("example", Bindable.of(Example.class), + new NoUnboundElementsBindHandler(true)); + } + public static class Example { private String foo;