Merge branch '2.4.x'

Closes gh-25862
pull/25873/head
Andy Wilkinson 4 years ago
commit 6a7de18101

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2020 the original author or authors. * Copyright 2012-2021 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -24,12 +24,14 @@ import javax.validation.Validator;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfigurationTests.CustomValidatorConfiguration.TestBeanPostProcessor; import org.springframework.boot.autoconfigure.validation.ValidationAutoConfigurationTests.CustomValidatorConfiguration.TestBeanPostProcessor;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter; import org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -55,171 +57,143 @@ import static org.mockito.Mockito.mock;
*/ */
class ValidationAutoConfigurationTests { class ValidationAutoConfigurationTests {
private AnnotationConfigApplicationContext context; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner(
AnnotationConfigApplicationContext::new)
@AfterEach .withConfiguration(AutoConfigurations.of(ValidationAutoConfiguration.class));
void close() {
if (this.context != null) {
this.context.close();
}
}
@Test @Test
void validationAutoConfigurationShouldConfigureDefaultValidator() { void validationAutoConfigurationShouldConfigureDefaultValidator() {
load(Config.class); this.contextRunner.run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
.getBeanNamesForType(org.springframework.validation.Validator.class); .containsExactly("defaultValidator");
assertThat(jsrValidatorNames).containsExactly("defaultValidator"); assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class)
assertThat(springValidatorNames).containsExactly("defaultValidator"); .isEqualTo(context.getBean(org.springframework.validation.Validator.class));
Validator jsrValidator = this.context.getBean(Validator.class); assertThat(isPrimaryBean(context, "defaultValidator")).isTrue();
org.springframework.validation.Validator springValidator = this.context });
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("defaultValidator")).isTrue();
} }
@Test @Test
void validationAutoConfigurationWhenUserProvidesValidatorShouldBackOff() { void validationAutoConfigurationWhenUserProvidesValidatorShouldBackOff() {
load(UserDefinedValidatorConfig.class); this.contextRunner.withUserConfiguration(UserDefinedValidatorConfig.class).run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("customValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
.getBeanNamesForType(org.springframework.validation.Validator.class); .containsExactly("customValidator");
assertThat(jsrValidatorNames).containsExactly("customValidator"); assertThat(context.getBean(Validator.class)).isInstanceOf(OptionalValidatorFactoryBean.class)
assertThat(springValidatorNames).containsExactly("customValidator"); .isEqualTo(context.getBean(org.springframework.validation.Validator.class));
org.springframework.validation.Validator springValidator = this.context assertThat(isPrimaryBean(context, "customValidator")).isFalse();
.getBean(org.springframework.validation.Validator.class); });
Validator jsrValidator = this.context.getBean(Validator.class);
assertThat(jsrValidator).isInstanceOf(OptionalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("customValidator")).isFalse();
} }
@Test @Test
void validationAutoConfigurationWhenUserProvidesDefaultValidatorShouldNotEnablePrimary() { void validationAutoConfigurationWhenUserProvidesDefaultValidatorShouldNotEnablePrimary() {
load(UserDefinedDefaultValidatorConfig.class); this.contextRunner.withUserConfiguration(UserDefinedDefaultValidatorConfig.class).run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
.getBeanNamesForType(org.springframework.validation.Validator.class); .containsExactly("defaultValidator");
assertThat(jsrValidatorNames).containsExactly("defaultValidator"); assertThat(isPrimaryBean(context, "defaultValidator")).isFalse();
assertThat(springValidatorNames).containsExactly("defaultValidator"); });
assertThat(isPrimaryBean("defaultValidator")).isFalse();
} }
@Test @Test
void validationAutoConfigurationWhenUserProvidesJsrValidatorShouldBackOff() { void validationAutoConfigurationWhenUserProvidesJsrValidatorShouldBackOff() {
load(UserDefinedJsrValidatorConfig.class); this.contextRunner.withUserConfiguration(UserDefinedJsrValidatorConfig.class).run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("customValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class)).isEmpty();
.getBeanNamesForType(org.springframework.validation.Validator.class); assertThat(isPrimaryBean(context, "customValidator")).isFalse();
assertThat(jsrValidatorNames).containsExactly("customValidator"); });
assertThat(springValidatorNames).isEmpty();
assertThat(isPrimaryBean("customValidator")).isFalse();
} }
@Test @Test
void validationAutoConfigurationWhenUserProvidesSpringValidatorShouldCreateJsrValidator() { void validationAutoConfigurationWhenUserProvidesSpringValidatorShouldCreateJsrValidator() {
load(UserDefinedSpringValidatorConfig.class); this.contextRunner.withUserConfiguration(UserDefinedSpringValidatorConfig.class).run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
.getBeanNamesForType(org.springframework.validation.Validator.class); .containsExactly("customValidator", "anotherCustomValidator", "defaultValidator");
assertThat(jsrValidatorNames).containsExactly("defaultValidator"); assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class)
assertThat(springValidatorNames).containsExactly("customValidator", "anotherCustomValidator", .isEqualTo(context.getBean(org.springframework.validation.Validator.class));
"defaultValidator"); assertThat(isPrimaryBean(context, "defaultValidator")).isTrue();
Validator jsrValidator = this.context.getBean(Validator.class); });
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(isPrimaryBean("defaultValidator")).isTrue();
} }
@Test @Test
void validationAutoConfigurationWhenUserProvidesPrimarySpringValidatorShouldRemovePrimaryFlag() { void validationAutoConfigurationWhenUserProvidesPrimarySpringValidatorShouldRemovePrimaryFlag() {
load(UserDefinedPrimarySpringValidatorConfig.class); this.contextRunner.withUserConfiguration(UserDefinedPrimarySpringValidatorConfig.class).run((context) -> {
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class); assertThat(context.getBeanNamesForType(Validator.class)).containsExactly("defaultValidator");
String[] springValidatorNames = this.context assertThat(context.getBeanNamesForType(org.springframework.validation.Validator.class))
.getBeanNamesForType(org.springframework.validation.Validator.class); .containsExactly("customValidator", "anotherCustomValidator", "defaultValidator");
assertThat(jsrValidatorNames).containsExactly("defaultValidator"); assertThat(context.getBean(Validator.class)).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(springValidatorNames).containsExactly("customValidator", "anotherCustomValidator", assertThat(context.getBean(org.springframework.validation.Validator.class))
"defaultValidator"); .isEqualTo(context.getBean("anotherCustomValidator"));
Validator jsrValidator = this.context.getBean(Validator.class); assertThat(isPrimaryBean(context, "defaultValidator")).isFalse();
org.springframework.validation.Validator springValidator = this.context });
.getBean(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(springValidator).isEqualTo(this.context.getBean("anotherCustomValidator"));
assertThat(isPrimaryBean("defaultValidator")).isFalse();
} }
@Test @Test
void validationIsEnabled() { void validationIsEnabled() {
load(SampleService.class); this.contextRunner.withUserConfiguration(SampleService.class).run((context) -> {
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
SampleService service = this.context.getBean(SampleService.class); SampleService service = context.getBean(SampleService.class);
service.doSomething("Valid"); service.doSomething("Valid");
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething("KO")); assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething("KO"));
});
} }
@Test @Test
void classCanBeExcludedFromValidation() { void classCanBeExcludedFromValidation() {
load(ExcludedServiceConfiguration.class); this.contextRunner.withUserConfiguration(ExcludedServiceConfiguration.class).run((context) -> {
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
ExcludedService service = this.context.getBean(ExcludedService.class); ExcludedService service = context.getBean(ExcludedService.class);
service.doSomething("Valid"); service.doSomething("Valid");
assertThatNoException().isThrownBy(() -> service.doSomething("KO")); assertThatNoException().isThrownBy(() -> service.doSomething("KO"));
});
} }
@Test @Test
void validationUsesCglibProxy() { void validationUsesCglibProxy() {
load(DefaultAnotherSampleService.class); this.contextRunner.withUserConfiguration(DefaultAnotherSampleService.class).run((context) -> {
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
DefaultAnotherSampleService service = this.context.getBean(DefaultAnotherSampleService.class); DefaultAnotherSampleService service = context.getBean(DefaultAnotherSampleService.class);
service.doSomething(42); service.doSomething(42);
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2)); assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2));
});
} }
@Test @Test
void validationCanBeConfiguredToUseJdkProxy() { void validationCanBeConfiguredToUseJdkProxy() {
load(AnotherSampleServiceConfiguration.class, "spring.aop.proxy-target-class=false"); this.contextRunner.withUserConfiguration(AnotherSampleServiceConfiguration.class)
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); .withPropertyValues("spring.aop.proxy-target-class=false").run((context) -> {
assertThat(this.context.getBeansOfType(DefaultAnotherSampleService.class)).isEmpty(); assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
AnotherSampleService service = this.context.getBean(AnotherSampleService.class); assertThat(context.getBeansOfType(DefaultAnotherSampleService.class)).isEmpty();
AnotherSampleService service = context.getBean(AnotherSampleService.class);
service.doSomething(42); service.doSomething(42);
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> service.doSomething(2)); assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> service.doSomething(2));
});
} }
@Test @Test
void userDefinedMethodValidationPostProcessorTakesPrecedence() { void userDefinedMethodValidationPostProcessorTakesPrecedence() {
load(SampleConfiguration.class); this.contextRunner.withUserConfiguration(SampleConfiguration.class).run((context) -> {
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(context.getBeansOfType(Validator.class)).hasSize(1);
Object userMethodValidationPostProcessor = this.context.getBean("testMethodValidationPostProcessor"); Object userMethodValidationPostProcessor = context.getBean("testMethodValidationPostProcessor");
assertThat(this.context.getBean(MethodValidationPostProcessor.class)) assertThat(context.getBean(MethodValidationPostProcessor.class))
.isSameAs(userMethodValidationPostProcessor); .isSameAs(userMethodValidationPostProcessor);
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)).hasSize(1); assertThat(context.getBeansOfType(MethodValidationPostProcessor.class)).hasSize(1);
assertThat(this.context.getBean(Validator.class)) assertThat(context.getBean(Validator.class))
.isNotSameAs(ReflectionTestUtils.getField(userMethodValidationPostProcessor, "validator")); .isNotSameAs(ReflectionTestUtils.getField(userMethodValidationPostProcessor, "validator"));
});
} }
@Test @Test
void methodValidationPostProcessorValidatorDependencyDoesNotTriggerEarlyInitialization() { void methodValidationPostProcessorValidatorDependencyDoesNotTriggerEarlyInitialization() {
load(CustomValidatorConfiguration.class); this.contextRunner.withUserConfiguration(CustomValidatorConfiguration.class)
assertThat(this.context.getBean(TestBeanPostProcessor.class).postProcessed).contains("someService"); .run((context) -> assertThat(context.getBean(TestBeanPostProcessor.class).postProcessed)
.contains("someService"));
} }
private boolean isPrimaryBean(String beanName) { private boolean isPrimaryBean(AssertableApplicationContext context, String beanName) {
return this.context.getBeanDefinition(beanName).isPrimary(); return ((BeanDefinitionRegistry) context.getSourceApplicationContext()).getBeanDefinition(beanName).isPrimary();
}
private void load(Class<?> config, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).applyTo(ctx);
if (config != null) {
ctx.register(config);
}
ctx.register(ValidationAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
} }
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)

Loading…
Cancel
Save