Validate constructor bound config props that implement Validator
Closes gh-33669
This commit is contained in:
parent
836d88c9c8
commit
e779fb0bc4
|
@ -51,6 +51,7 @@ import org.springframework.context.ConfigurableApplicationContext;
|
|||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.validation.Errors;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
|
@ -136,6 +137,7 @@ class ConfigurationPropertiesBinder {
|
|||
: new IgnoreTopLevelConverterNotFoundBindHandler();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Validator> getValidators(Bindable<?> target) {
|
||||
List<Validator> validators = new ArrayList<>(3);
|
||||
if (this.configurationPropertiesValidator != null) {
|
||||
|
@ -144,8 +146,13 @@ class ConfigurationPropertiesBinder {
|
|||
if (this.jsr303Present && target.getAnnotation(Validated.class) != null) {
|
||||
validators.add(getJsr303Validator());
|
||||
}
|
||||
if (target.getValue() != null && target.getValue().get() instanceof Validator) {
|
||||
validators.add((Validator) target.getValue().get());
|
||||
if (target.getValue() != null) {
|
||||
if (target.getValue().get() instanceof Validator) {
|
||||
validators.add((Validator) target.getValue().get());
|
||||
}
|
||||
}
|
||||
else if (Validator.class.isAssignableFrom(target.getType().resolve())) {
|
||||
validators.add(new SelfValidatingConstructorBoundBindableValidator((Bindable<? extends Validator>) target));
|
||||
}
|
||||
return validators;
|
||||
}
|
||||
|
@ -258,4 +265,28 @@ class ConfigurationPropertiesBinder {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Validator} for a constructor-bound {@code Bindable} where the type being
|
||||
* bound is itself a {@code Validator} implementation.
|
||||
*/
|
||||
static class SelfValidatingConstructorBoundBindableValidator implements Validator {
|
||||
|
||||
private final Bindable<? extends Validator> bindable;
|
||||
|
||||
SelfValidatingConstructorBoundBindableValidator(Bindable<? extends Validator> bindable) {
|
||||
this.bindable = bindable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return clazz.isAssignableFrom(this.bindable.getType().resolve());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
((Validator) target).validate(target, errors);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -731,6 +731,16 @@ class ConfigurationPropertiesTests {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadWhenConstructorBoundConfigurationPropertiesIsAlsoValidatorShouldApplyValidator() {
|
||||
assertThatExceptionOfType(Exception.class)
|
||||
.isThrownBy(() -> load(ValidatorConstructorBoundPropertiesConfiguration.class))
|
||||
.satisfies((ex) -> {
|
||||
assertThat(ex).hasCauseInstanceOf(BindException.class);
|
||||
assertThat(ex.getCause()).hasCauseExactlyInstanceOf(BindValidationException.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadWhenConfigurationPropertiesWithValidDefaultValuesShouldNotFail() {
|
||||
AnnotationConfigApplicationContext context = load(ValidatorPropertiesWithDefaultValues.class);
|
||||
|
@ -2025,6 +2035,37 @@ class ConfigurationPropertiesTests {
|
|||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties(ValidatorConstructorBoundProperties.class)
|
||||
static class ValidatorConstructorBoundPropertiesConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConstructorBinding
|
||||
@ConfigurationProperties
|
||||
static class ValidatorConstructorBoundProperties implements Validator {
|
||||
|
||||
private final String foo;
|
||||
|
||||
ValidatorConstructorBoundProperties(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> type) {
|
||||
return type == ValidatorConstructorBoundProperties.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(Object target, Errors errors) {
|
||||
ValidationUtils.rejectIfEmpty(errors, "foo", "TEST1");
|
||||
}
|
||||
|
||||
String getFoo() {
|
||||
return this.foo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableConfigurationProperties
|
||||
@ConfigurationProperties(prefix = "test")
|
||||
static class WithSetterThatThrowsValidationExceptionProperties {
|
||||
|
|
Loading…
Reference in New Issue