Merge pull request #29429 from dangzhicairang

* gh-29429:
  Polish "Provide callback mechanism for customizing validation configuration"
  Provide callback mechanism for customizing validation configuration

Closes gh-29429
pull/30862/head
Andy Wilkinson 3 years ago
commit f12dcb51ab

@ -56,8 +56,11 @@ public class ValidationAutoConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext) {
public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext,
ObjectProvider<ValidationConfigurationCustomizer> customizers) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setConfigurationInitializer((configuration) -> customizers.orderedStream()
.forEach((customizer) -> customizer.customize(configuration)));
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(applicationContext);
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;

@ -0,0 +1,36 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.validation;
import jakarta.validation.Configuration;
/**
* Callback interface that can be used to customize {@link Configuration}.
*
* @author Dang Zhicairang
* @since 3.0.0
*/
@FunctionalInterface
public interface ValidationConfigurationCustomizer {
/**
* Customize the given {@code configuration}.
* @param configuration the configuration to customize
*/
void customize(Configuration<?> configuration);
}

@ -24,6 +24,8 @@ import jakarta.validation.Validator;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Size;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.BeanPostProcessor;
@ -36,6 +38,7 @@ import org.springframework.boot.validation.beanvalidation.MethodValidationExclud
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.CustomValidatorBean;
@ -46,6 +49,8 @@ import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBea
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
@ -235,6 +240,19 @@ class ValidationAutoConfigurationTests {
}));
}
@Test
void configurationCustomizerBeansAreCalledInOrder() {
this.contextRunner.withUserConfiguration(ConfigurationCustomizersConfiguration.class).run((context) -> {
ValidationConfigurationCustomizer customizerOne = context.getBean("customizerOne",
ValidationConfigurationCustomizer.class);
ValidationConfigurationCustomizer customizerTwo = context.getBean("customizerTwo",
ValidationConfigurationCustomizer.class);
InOrder inOrder = Mockito.inOrder(customizerOne, customizerTwo);
then(customizerTwo).should(inOrder).customize(any(jakarta.validation.Configuration.class));
then(customizerOne).should(inOrder).customize(any(jakarta.validation.Configuration.class));
});
}
private boolean isPrimaryBean(AssertableApplicationContext context, String beanName) {
return ((BeanDefinitionRegistry) context.getSourceApplicationContext()).getBeanDefinition(beanName).isPrimary();
}
@ -421,4 +439,21 @@ class ValidationAutoConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class ConfigurationCustomizersConfiguration {
@Bean
@Order(1)
ValidationConfigurationCustomizer customizerOne() {
return mock(ValidationConfigurationCustomizer.class);
}
@Bean
@Order(0)
ValidationConfigurationCustomizer customizerTwo() {
return mock(ValidationConfigurationCustomizer.class);
}
}
}

@ -11,3 +11,6 @@ include::code:MyBean[]
The application's `MessageSource` is used when resolving `+{parameters}+` in constraint messages.
This allows you to use <<features.adoc#features.internationalization,your application's `messages.properties` files>> for Bean Validation messages.
Once the parameters have been resolved, message interpolation is completed using Bean Validation's default interpolator.
To customize the `Configuration` used to build the `ValidatorFactory`, define a `ValidationConfigurationCustomizer` bean.
When multiple customizer beans are defined, they are called in order based on their `@Order` annotation or `Ordered` implementation.
Loading…
Cancel
Save