From 76a1c6bcaabc6ad524a762547a9a445fb61b73d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=93=E6=99=BA=E6=89=8D=E8=AE=A9?= <1137729123@qq.com> Date: Sun, 16 Jan 2022 16:02:17 +0800 Subject: [PATCH 1/2] Provide callback mechanism for customizing validation configuration See gh-29429 --- .../ValidationAutoConfiguration.java | 19 +++++- ...CustomizableLocalValidatorFactoryBean.java | 59 +++++++++++++++++++ .../AddValueExtractorCustomizer.java | 54 +++++++++++++++++ .../beanvalidation/customize/Customizer.java | 33 +++++++++++ 4 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java index ccf276c5e61..8d44afd9631 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java @@ -16,8 +16,11 @@ package org.springframework.boot.autoconfigure.validation; +import java.util.List; + import jakarta.validation.Validator; import jakarta.validation.executable.ExecutableValidator; +import jakarta.validation.valueextraction.ValueExtractor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanDefinition; @@ -28,8 +31,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.validation.MessageInterpolatorFactory; +import org.springframework.boot.validation.beanvalidation.CustomizableLocalValidatorFactoryBean; import org.springframework.boot.validation.beanvalidation.FilteredMethodValidationPostProcessor; import org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter; +import org.springframework.boot.validation.beanvalidation.customize.AddValueExtractorCustomizer; +import org.springframework.boot.validation.beanvalidation.customize.Customizer; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -53,11 +59,20 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess @Import(PrimaryDefaultValidatorPostProcessor.class) public class ValidationAutoConfiguration { + @Bean + public static AddValueExtractorCustomizer autoAddValueExtractorCustomizer( + @SuppressWarnings({ "rawtypes", "unchecked" }) List valueExtractors) { + + return new AddValueExtractorCustomizer(valueExtractors); + } + @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @ConditionalOnMissingBean(Validator.class) - public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext) { - LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); + public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext, + List customizers) { + CustomizableLocalValidatorFactoryBean factoryBean = new CustomizableLocalValidatorFactoryBean(); + factoryBean.setCustomizers(customizers); MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(applicationContext); factoryBean.setMessageInterpolator(interpolatorFactory.getObject()); return factoryBean; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java new file mode 100644 index 00000000000..bc31f60ef9f --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2020 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.validation.beanvalidation; + +import java.util.List; +import java.util.Optional; + +import jakarta.validation.Configuration; + +import org.springframework.boot.validation.beanvalidation.customize.Customizer; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; + +/** + * This class will callback the {@link Customizer} which supply by application. For + * example: + * + *
{@code
+ * @Bean
+ * public Customizer customizer() {
+ *		return configuration -> {
+ *			configuration.addValueExtractor(new CustomResultValueExtractor());
+ *			configuration.xxx
+ *			...
+ *		};
+ * }
+ * }
+ * + * @author Dang Zhicairang + * @since 2.6.2 + */ +public class CustomizableLocalValidatorFactoryBean extends LocalValidatorFactoryBean { + + private List customizers; + + public void setCustomizers(List customizers) { + this.customizers = customizers; + } + + @Override + protected void postProcessConfiguration(Configuration configuration) { + Optional.ofNullable(this.customizers) + .ifPresent((list) -> list.forEach((customizer) -> customizer.customize(configuration))); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java new file mode 100644 index 00000000000..6854d8933b7 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012-2020 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.validation.beanvalidation.customize; + +import java.util.List; +import java.util.Optional; + +import jakarta.validation.Configuration; +import jakarta.validation.valueextraction.ValueExtractor; + +/** + * Add given collection of {@link ValueExtractor} into {@link Configuration}. + * + * @author Dang Zhicairang + * @since 2.6.2 + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +public class AddValueExtractorCustomizer implements Customizer { + + private List valueExtractors; + + public AddValueExtractorCustomizer(List valueExtractors) { + this.valueExtractors = valueExtractors; + } + + public List getValueExtractors() { + return this.valueExtractors; + } + + public void setValueExtractors(List valueExtractors) { + this.valueExtractors = valueExtractors; + } + + @Override + public void customize(Configuration configuration) { + Optional.ofNullable(this.getValueExtractors()) + .ifPresent((list) -> list.forEach(configuration::addValueExtractor)); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java new file mode 100644 index 00000000000..f00c6a1dd61 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012-2020 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.validation.beanvalidation.customize; + +import jakarta.validation.Configuration; + +/** + * Callback interface that can be used to customize {@link Configuration}. + * + * @author Dang Zhicairang + * @since 2.6.2 + * @see AddValueExtractorCustomizer + */ +@FunctionalInterface +public interface Customizer { + + void customize(Configuration configuration); + +} From 0e00fafe385256d5da1e5a26fd8fd97173ba8d32 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 4 May 2022 11:36:43 +0100 Subject: [PATCH 2/2] Polish "Provide callback mechanism for customizing validation configuration" See gh-29429 --- .../ValidationAutoConfiguration.java | 20 ++----- .../ValidationConfigurationCustomizer.java} | 13 ++-- .../ValidationAutoConfigurationTests.java | 35 +++++++++++ .../src/docs/asciidoc/io/validation.adoc | 3 + ...CustomizableLocalValidatorFactoryBean.java | 59 ------------------- .../AddValueExtractorCustomizer.java | 54 ----------------- 6 files changed, 50 insertions(+), 134 deletions(-) rename spring-boot-project/{spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java => spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationConfigurationCustomizer.java} (72%) delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java delete mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java index 8d44afd9631..a1d046b996c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java @@ -16,11 +16,8 @@ package org.springframework.boot.autoconfigure.validation; -import java.util.List; - import jakarta.validation.Validator; import jakarta.validation.executable.ExecutableValidator; -import jakarta.validation.valueextraction.ValueExtractor; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanDefinition; @@ -31,11 +28,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.validation.MessageInterpolatorFactory; -import org.springframework.boot.validation.beanvalidation.CustomizableLocalValidatorFactoryBean; import org.springframework.boot.validation.beanvalidation.FilteredMethodValidationPostProcessor; import org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter; -import org.springframework.boot.validation.beanvalidation.customize.AddValueExtractorCustomizer; -import org.springframework.boot.validation.beanvalidation.customize.Customizer; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @@ -59,20 +53,14 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess @Import(PrimaryDefaultValidatorPostProcessor.class) public class ValidationAutoConfiguration { - @Bean - public static AddValueExtractorCustomizer autoAddValueExtractorCustomizer( - @SuppressWarnings({ "rawtypes", "unchecked" }) List valueExtractors) { - - return new AddValueExtractorCustomizer(valueExtractors); - } - @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @ConditionalOnMissingBean(Validator.class) public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext, - List customizers) { - CustomizableLocalValidatorFactoryBean factoryBean = new CustomizableLocalValidatorFactoryBean(); - factoryBean.setCustomizers(customizers); + ObjectProvider 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; diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationConfigurationCustomizer.java similarity index 72% rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationConfigurationCustomizer.java index f00c6a1dd61..e639cda846e 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/Customizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationConfigurationCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.validation.beanvalidation.customize; +package org.springframework.boot.autoconfigure.validation; import jakarta.validation.Configuration; @@ -22,12 +22,15 @@ import jakarta.validation.Configuration; * Callback interface that can be used to customize {@link Configuration}. * * @author Dang Zhicairang - * @since 2.6.2 - * @see AddValueExtractorCustomizer + * @since 3.0.0 */ @FunctionalInterface -public interface Customizer { +public interface ValidationConfigurationCustomizer { + /** + * Customize the given {@code configuration}. + * @param configuration the configuration to customize + */ void customize(Configuration configuration); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfigurationTests.java index 717a7b18f0c..6d9f801594a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfigurationTests.java @@ -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); + } + + } + } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/validation.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/validation.adoc index f9561871b8c..be6929a7bd0 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/validation.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/io/validation.adoc @@ -11,3 +11,6 @@ include::code:MyBean[] The application's `MessageSource` is used when resolving `+{parameters}+` in constraint messages. This allows you to use <> 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. \ No newline at end of file diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java deleted file mode 100644 index bc31f60ef9f..00000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/CustomizableLocalValidatorFactoryBean.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012-2020 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.validation.beanvalidation; - -import java.util.List; -import java.util.Optional; - -import jakarta.validation.Configuration; - -import org.springframework.boot.validation.beanvalidation.customize.Customizer; -import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; - -/** - * This class will callback the {@link Customizer} which supply by application. For - * example: - * - *
{@code
- * @Bean
- * public Customizer customizer() {
- *		return configuration -> {
- *			configuration.addValueExtractor(new CustomResultValueExtractor());
- *			configuration.xxx
- *			...
- *		};
- * }
- * }
- * - * @author Dang Zhicairang - * @since 2.6.2 - */ -public class CustomizableLocalValidatorFactoryBean extends LocalValidatorFactoryBean { - - private List customizers; - - public void setCustomizers(List customizers) { - this.customizers = customizers; - } - - @Override - protected void postProcessConfiguration(Configuration configuration) { - Optional.ofNullable(this.customizers) - .ifPresent((list) -> list.forEach((customizer) -> customizer.customize(configuration))); - } - -} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java deleted file mode 100644 index 6854d8933b7..00000000000 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/validation/beanvalidation/customize/AddValueExtractorCustomizer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012-2020 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.validation.beanvalidation.customize; - -import java.util.List; -import java.util.Optional; - -import jakarta.validation.Configuration; -import jakarta.validation.valueextraction.ValueExtractor; - -/** - * Add given collection of {@link ValueExtractor} into {@link Configuration}. - * - * @author Dang Zhicairang - * @since 2.6.2 - */ -@SuppressWarnings({ "rawtypes", "unchecked" }) -public class AddValueExtractorCustomizer implements Customizer { - - private List valueExtractors; - - public AddValueExtractorCustomizer(List valueExtractors) { - this.valueExtractors = valueExtractors; - } - - public List getValueExtractors() { - return this.valueExtractors; - } - - public void setValueExtractors(List valueExtractors) { - this.valueExtractors = valueExtractors; - } - - @Override - public void customize(Configuration configuration) { - Optional.ofNullable(this.getValueExtractors()) - .ifPresent((list) -> list.forEach(configuration::addValueExtractor)); - } - -}