From 145400505daf3dce313f92b2e621e37d59aea43b Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 26 Feb 2018 23:08:45 -0800 Subject: [PATCH] Only validate target values on zero depth Update `ValidationBindHandler` so that validation only occurs when a value is bound, or the bind depth is zero. This prevents validation from triggering getters which may throw an exception. Fixes gh-12227 --- .../validation/ValidationBindHandler.java | 11 +++++---- .../ValidationBindHandlerTests.java | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java index ba98387f12..dcf5faf0c4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandler.java @@ -69,7 +69,7 @@ public class ValidationBindHandler extends AbstractBindHandler { @Override public void onFinish(ConfigurationPropertyName name, Bindable target, BindContext context, Object result) throws Exception { - validate(name, target, result); + validate(name, target, context, result); if (context.getDepth() == 0 && !this.exceptions.isEmpty()) { throw this.exceptions.pop(); } @@ -77,17 +77,18 @@ public class ValidationBindHandler extends AbstractBindHandler { } private void validate(ConfigurationPropertyName name, Bindable target, - Object result) { - Object validationTarget = getValidationTarget(target, result); + BindContext context, Object result) { + Object validationTarget = getValidationTarget(target, context, result); Class validationType = target.getBoxedType().resolve(); validate(name, validationTarget, validationType); } - private Object getValidationTarget(Bindable target, Object result) { + private Object getValidationTarget(Bindable target, BindContext context, + Object result) { if (result != null) { return result; } - if (target.getValue() != null) { + if (context.getDepth() == 0 && target.getValue() != null) { return target.getValue().get(); } return null; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java index 3d4a70a33c..1c27f804e7 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/validation/ValidationBindHandlerTests.java @@ -153,6 +153,19 @@ public class ValidationBindHandlerTests { this.handler)); } + @Test + public void bindShouldNotValidateDepthGreaterThanZero() { + // gh-12227 + MockConfigurationPropertySource source = new MockConfigurationPropertySource(); + source.put("foo.bar", "baz"); + this.sources.add(source); + ExampleValidatedBeanWithGetterException existingValue = new ExampleValidatedBeanWithGetterException(); + this.binder.bind(ConfigurationPropertyName.of("foo"), + Bindable.of(ExampleValidatedBeanWithGetterException.class) + .withExistingValue(existingValue), + this.handler); + } + private BindValidationException bindAndExpectValidationError(Runnable action) { try { action.run(); @@ -244,6 +257,16 @@ public class ValidationBindHandlerTests { public void setAddress(String address) { this.address = address; } + + } + + @Validated + public static class ExampleValidatedBeanWithGetterException { + + public int getAge() { + throw new RuntimeException(); + } + } }