Merge pull request #37119 from zpavloudis

* gh-37119:
  Polish "Support unwrapping in ValidatorAdapter"
  Support unwrapping in ValidatorAdapter

Closes gh-37119
This commit is contained in:
Andy Wilkinson 2023-09-11 11:22:04 +01:00
commit 04c9c166e6
2 changed files with 88 additions and 0 deletions

View File

@ -39,6 +39,7 @@ import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
*
* @author Stephane Nicoll
* @author Phillip Webb
* @author Zisis Pavloudis
* @since 2.0.0
*/
public class ValidatorAdapter implements SmartValidator, ApplicationContextAware, InitializingBean, DisposableBean {
@ -153,4 +154,13 @@ public class ValidatorAdapter implements SmartValidator, ApplicationContextAware
return validator;
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> type) {
if (type.isInstance(this.target)) {
return (T) this.target;
}
return this.target.unwrap(type);
}
}

View File

@ -18,7 +18,9 @@ package org.springframework.boot.autoconfigure.validation;
import java.util.HashMap;
import jakarta.validation.Validator;
import jakarta.validation.constraints.Min;
import org.hibernate.validator.HibernateValidator;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.FilteredClassLoader;
@ -27,10 +29,13 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.validation.Errors;
import org.springframework.validation.MapBindingResult;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
@ -91,6 +96,30 @@ class ValidatorAdapterTests {
.run((context) -> ValidatorAdapter.get(context, null));
}
@Test
void unwrapToJakartaValidatorShouldReturnJakartaValidator() {
this.contextRunner.withUserConfiguration(LocalValidatorFactoryBeanConfig.class).run((context) -> {
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
assertThat(wrapper.unwrap(Validator.class)).isInstanceOf(Validator.class);
});
}
@Test
void whenJakartaValidatorIsWrappedMultipleTimesUnwrapToJakartaValidatorShouldReturnJakartaValidator() {
this.contextRunner.withUserConfiguration(DoubleWrappedConfig.class).run((context) -> {
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
assertThat(wrapper.unwrap(Validator.class)).isInstanceOf(Validator.class);
});
}
@Test
void unwrapToUnsupportedTypeShouldThrow() {
this.contextRunner.withUserConfiguration(LocalValidatorFactoryBeanConfig.class).run((context) -> {
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
assertThatRuntimeException().isThrownBy(() -> wrapper.unwrap(HibernateValidator.class));
});
}
@Configuration(proxyBeanMethods = false)
static class LocalValidatorFactoryBeanConfig {
@ -106,6 +135,55 @@ class ValidatorAdapterTests {
}
@Configuration(proxyBeanMethods = false)
static class DoubleWrappedConfig {
@Bean
LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
ValidatorAdapter wrapper(LocalValidatorFactoryBean validator) {
return new ValidatorAdapter(new Wrapper(validator), true);
}
static class Wrapper implements SmartValidator {
private final SmartValidator delegate;
Wrapper(SmartValidator delegate) {
this.delegate = delegate;
}
@Override
public boolean supports(Class<?> clazz) {
return this.delegate.supports(clazz);
}
@Override
public void validate(Object target, Errors errors) {
this.delegate.validate(target, errors);
}
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
this.delegate.validate(target, errors, validationHints);
}
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> type) {
if (type.isInstance(this.delegate)) {
return (T) this.delegate;
}
return this.delegate.unwrap(type);
}
}
}
@Configuration(proxyBeanMethods = false)
static class NonManagedBeanConfig {