Skip to content

Commit eeea065

Browse files
committed
Merge branch '2.7.x' into 3.0.x
Closes gh-35912
2 parents 19b3ce9 + e779fb0 commit eeea065

File tree

2 files changed

+73
-2
lines changed

2 files changed

+73
-2
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBinder.java

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.springframework.core.convert.ConversionService;
5252
import org.springframework.core.env.PropertySources;
5353
import org.springframework.util.Assert;
54+
import org.springframework.validation.Errors;
5455
import org.springframework.validation.Validator;
5556
import org.springframework.validation.annotation.Validated;
5657

@@ -134,6 +135,7 @@ private IgnoreTopLevelConverterNotFoundBindHandler getHandler() {
134135
: new IgnoreTopLevelConverterNotFoundBindHandler();
135136
}
136137

138+
@SuppressWarnings("unchecked")
137139
private List<Validator> getValidators(Bindable<?> target) {
138140
List<Validator> validators = new ArrayList<>(3);
139141
if (this.configurationPropertiesValidator != null) {
@@ -142,8 +144,13 @@ private List<Validator> getValidators(Bindable<?> target) {
142144
if (this.jsr303Present && target.getAnnotation(Validated.class) != null) {
143145
validators.add(getJsr303Validator());
144146
}
145-
if (target.getValue() != null && target.getValue().get() instanceof Validator validator) {
146-
validators.add(validator);
147+
if (target.getValue() != null) {
148+
if (target.getValue().get() instanceof Validator validator) {
149+
validators.add(validator);
150+
}
151+
}
152+
else if (Validator.class.isAssignableFrom(target.getType().resolve())) {
153+
validators.add(new SelfValidatingConstructorBoundBindableValidator((Bindable<? extends Validator>) target));
147154
}
148155
return validators;
149156
}
@@ -250,4 +257,28 @@ public ConfigurationPropertiesBinder getObject() throws Exception {
250257

251258
}
252259

260+
/**
261+
* A {@code Validator} for a constructor-bound {@code Bindable} where the type being
262+
* bound is itself a {@code Validator} implementation.
263+
*/
264+
static class SelfValidatingConstructorBoundBindableValidator implements Validator {
265+
266+
private final Bindable<? extends Validator> bindable;
267+
268+
SelfValidatingConstructorBoundBindableValidator(Bindable<? extends Validator> bindable) {
269+
this.bindable = bindable;
270+
}
271+
272+
@Override
273+
public boolean supports(Class<?> clazz) {
274+
return clazz.isAssignableFrom(this.bindable.getType().resolve());
275+
}
276+
277+
@Override
278+
public void validate(Object target, Errors errors) {
279+
((Validator) target).validate(target, errors);
280+
}
281+
282+
}
283+
253284
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,16 @@ void loadWhenConfigurationPropertiesIsAlsoValidatorShouldApplyValidator() {
731731
});
732732
}
733733

734+
@Test
735+
void loadWhenConstructorBoundConfigurationPropertiesIsAlsoValidatorShouldApplyValidator() {
736+
assertThatExceptionOfType(Exception.class)
737+
.isThrownBy(() -> load(ValidatorConstructorBoundPropertiesConfiguration.class))
738+
.satisfies((ex) -> {
739+
assertThat(ex).hasCauseInstanceOf(BindException.class);
740+
assertThat(ex.getCause()).hasCauseExactlyInstanceOf(BindValidationException.class);
741+
});
742+
}
743+
734744
@Test
735745
void loadWhenConfigurationPropertiesWithValidDefaultValuesShouldNotFail() {
736746
AnnotationConfigApplicationContext context = load(ValidatorPropertiesWithDefaultValues.class);
@@ -2060,6 +2070,36 @@ void setFoo(String foo) {
20602070

20612071
}
20622072

2073+
@EnableConfigurationProperties(ValidatorConstructorBoundProperties.class)
2074+
static class ValidatorConstructorBoundPropertiesConfiguration {
2075+
2076+
}
2077+
2078+
@ConfigurationProperties
2079+
static class ValidatorConstructorBoundProperties implements Validator {
2080+
2081+
private final String foo;
2082+
2083+
ValidatorConstructorBoundProperties(String foo) {
2084+
this.foo = foo;
2085+
}
2086+
2087+
@Override
2088+
public boolean supports(Class<?> type) {
2089+
return type == ValidatorConstructorBoundProperties.class;
2090+
}
2091+
2092+
@Override
2093+
public void validate(Object target, Errors errors) {
2094+
ValidationUtils.rejectIfEmpty(errors, "foo", "TEST1");
2095+
}
2096+
2097+
String getFoo() {
2098+
return this.foo;
2099+
}
2100+
2101+
}
2102+
20632103
@EnableConfigurationProperties
20642104
@ConfigurationProperties(prefix = "test")
20652105
static class WithSetterThatThrowsValidationExceptionProperties {

0 commit comments

Comments
 (0)