diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java index 58bc3c2f60e..33d53fcb787 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-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. @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.function.Consumer; import jakarta.validation.ClockProvider; import jakarta.validation.Configuration; @@ -106,6 +107,9 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter private final Map validationPropertyMap = new HashMap<>(); + @Nullable + private Consumer> configurationInitializer; + @Nullable private ApplicationContext applicationContext; @@ -227,6 +231,18 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter return this.validationPropertyMap; } + /** + * Specify a callback for customizing the Bean Validation {@code Configuration} instance, + * as an alternative to overriding the {@link #postProcessConfiguration(Configuration)} + * method in custom {@code LocalValidatorFactoryBean} subclasses. + *

This enables convenient customizations for application purposes. Infrastructure + * extensions may keep overriding the {@link #postProcessConfiguration} template method. + * @since 5.3.19 + */ + public void setConfigurationInitializer(Consumer> configurationInitializer) { + this.configurationInitializer = configurationInitializer; + } + @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; @@ -305,6 +321,9 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter this.validationPropertyMap.forEach(configuration::addProperty); // Allow for custom post-processing before we actually build the ValidatorFactory. + if (this.configurationInitializer != null) { + this.configurationInitializer.accept(configuration); + } postProcessConfiguration(configuration); try { diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java index 9b8b07a37e7..ffcb2909541 100644 --- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java +++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java @@ -31,6 +31,7 @@ import java.util.Set; import jakarta.validation.Constraint; import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import jakarta.validation.ConstraintValidatorFactory; import jakarta.validation.ConstraintViolation; import jakarta.validation.Payload; import jakarta.validation.Valid; @@ -42,6 +43,7 @@ import org.hibernate.validator.HibernateValidatorFactory; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.convert.support.DefaultConversionService; @@ -312,6 +314,32 @@ class ValidatorFactoryTests { validator.destroy(); } + @Test + void withConstraintValidatorFactory() { + ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory()); + + @SuppressWarnings("resource") + LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); + validator.setConstraintValidatorFactory(cvf); + validator.afterPropertiesSet(); + + assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf); + validator.destroy(); + } + + @Test + void withCustomInitializer() { + ConstraintValidatorFactory cvf = new SpringConstraintValidatorFactory(new DefaultListableBeanFactory()); + + @SuppressWarnings("resource") + LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); + validator.setConfigurationInitializer(configuration -> configuration.constraintValidatorFactory(cvf)); + validator.afterPropertiesSet(); + + assertThat(validator.getConstraintValidatorFactory()).isSameAs(cvf); + validator.destroy(); + } + @NameAddressValid public static class ValidPerson { @@ -408,8 +436,8 @@ class ValidatorFactoryTests { } boolean valid = (value.name == null || !value.address.street.contains(value.name)); if (!valid && "Phil".equals(value.name)) { - context.buildConstraintViolationWithTemplate( - context.getDefaultConstraintMessageTemplate()).addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) + .addPropertyNode("address").addConstraintViolation().disableDefaultConstraintViolation(); } return valid; } @@ -445,6 +473,7 @@ class ValidatorFactoryTests { public String getValue() { return value; } + public void setValue(String value) { this.value = value; } @@ -453,7 +482,7 @@ class ValidatorFactoryTests { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) - @Constraint(validatedBy=InnerValidator.class) + @Constraint(validatedBy = InnerValidator.class) public @interface InnerValid { String message() default "NOT VALID"; @@ -474,7 +503,8 @@ class ValidatorFactoryTests { public boolean isValid(InnerBean bean, ConstraintValidatorContext context) { context.disableDefaultConstraintViolation(); if (bean.getValue() == null) { - context.buildConstraintViolationWithTemplate("NULL").addPropertyNode("value").addConstraintViolation(); + context.buildConstraintViolationWithTemplate("NULL") + .addPropertyNode("value").addConstraintViolation(); return false; } return true; @@ -522,7 +552,8 @@ class ValidatorFactoryTests { boolean valid = true; for (int i = 0; i < list.size(); i++) { if ("X".equals(list.get(i))) { - context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()).addBeanNode().inIterable().atIndex(i).addConstraintViolation(); + context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()) + .addBeanNode().inIterable().atIndex(i).addConstraintViolation(); valid = false; } } diff --git a/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml b/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml index 9e7c75135c2..5f4b476586b 100644 --- a/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml +++ b/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml @@ -17,7 +17,7 @@ - + \ No newline at end of file diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java index 0277cf6050f..536fca16481 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java @@ -203,7 +203,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest { @Nullable protected SslInfo initSslInfo() { X509Certificate[] certificates = getX509Certificates(); - return certificates != null ? new DefaultSslInfo(getSslSessionId(), certificates) : null; + return (certificates != null ? new DefaultSslInfo(getSslSessionId(), certificates) : null); } @Nullable @@ -213,8 +213,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest { @Nullable private X509Certificate[] getX509Certificates() { - String name = "jakarta.servlet.request.X509Certificate"; - return (X509Certificate[]) this.request.getAttribute(name); + return (X509Certificate[]) this.request.getAttribute("jakarta.servlet.request.X509Certificate"); } @Override