Skip to content

Commit 4085425

Browse files
committed
Polish "Support unwrapping in ValidatorAdapter"
See gh-37119
1 parent eb6b151 commit 4085425

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapter.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,10 @@ private static Validator wrap(Validator validator, boolean existingBean) {
157157
@Override
158158
@SuppressWarnings("unchecked")
159159
public <T> T unwrap(Class<T> type) {
160-
if (type.isAssignableFrom(this.target.getClass())) {
161-
if (this.target instanceof SpringValidatorAdapter adapter) {
162-
return adapter.unwrap(type);
163-
}
160+
if (type.isInstance(this.target)) {
164161
return (T) this.target;
165162
}
166-
167-
throw new IllegalArgumentException("Cannot unwrap " + this.target + " to " + type.getName());
163+
return this.target.unwrap(type);
168164
}
169165

170166
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapterTests.java

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import jakarta.validation.Validator;
2222
import jakarta.validation.constraints.Min;
2323
import org.hibernate.validator.HibernateValidator;
24-
import org.junit.jupiter.api.Assertions;
2524
import org.junit.jupiter.api.Test;
2625

2726
import org.springframework.boot.test.context.FilteredClassLoader;
@@ -30,10 +29,13 @@
3029
import org.springframework.context.annotation.Bean;
3130
import org.springframework.context.annotation.Configuration;
3231
import org.springframework.core.io.ClassPathResource;
32+
import org.springframework.validation.Errors;
3333
import org.springframework.validation.MapBindingResult;
34+
import org.springframework.validation.SmartValidator;
3435
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
3536

3637
import static org.assertj.core.api.Assertions.assertThat;
38+
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
3739
import static org.mockito.ArgumentMatchers.any;
3840
import static org.mockito.BDDMockito.then;
3941
import static org.mockito.Mockito.mock;
@@ -95,11 +97,26 @@ void wrapperWhenValidationProviderNotPresentShouldNotThrowException() {
9597
}
9698

9799
@Test
98-
void unwrapValidatorInstanceOfJakartaTypeAndExceptionThrownWhenTypeNotSupported() {
100+
void unwrapToJakartaValidatorShouldReturnJakartaValidator() {
99101
this.contextRunner.withUserConfiguration(LocalValidatorFactoryBeanConfig.class).run((context) -> {
100102
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
101103
assertThat(wrapper.unwrap(Validator.class)).isInstanceOf(Validator.class);
102-
Assertions.assertThrows(IllegalArgumentException.class, () -> wrapper.unwrap(HibernateValidator.class));
104+
});
105+
}
106+
107+
@Test
108+
void whenJakartaValidatorIsWrappedMultipleTimesUnwrapToJakartaValidatorShouldReturnJakartaValidator() {
109+
this.contextRunner.withUserConfiguration(DoubleWrappedConfig.class).run((context) -> {
110+
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
111+
assertThat(wrapper.unwrap(Validator.class)).isInstanceOf(Validator.class);
112+
});
113+
}
114+
115+
@Test
116+
void unwrapToUnsupportedTypeShouldThrow() {
117+
this.contextRunner.withUserConfiguration(LocalValidatorFactoryBeanConfig.class).run((context) -> {
118+
ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class);
119+
assertThatRuntimeException().isThrownBy(() -> wrapper.unwrap(HibernateValidator.class));
103120
});
104121
}
105122

@@ -118,6 +135,55 @@ ValidatorAdapter wrapper(LocalValidatorFactoryBean validator) {
118135

119136
}
120137

138+
@Configuration(proxyBeanMethods = false)
139+
static class DoubleWrappedConfig {
140+
141+
@Bean
142+
LocalValidatorFactoryBean validator() {
143+
return new LocalValidatorFactoryBean();
144+
}
145+
146+
@Bean
147+
ValidatorAdapter wrapper(LocalValidatorFactoryBean validator) {
148+
return new ValidatorAdapter(new Wrapper(validator), true);
149+
}
150+
151+
static class Wrapper implements SmartValidator {
152+
153+
private final SmartValidator delegate;
154+
155+
Wrapper(SmartValidator delegate) {
156+
this.delegate = delegate;
157+
}
158+
159+
@Override
160+
public boolean supports(Class<?> clazz) {
161+
return this.delegate.supports(clazz);
162+
}
163+
164+
@Override
165+
public void validate(Object target, Errors errors) {
166+
this.delegate.validate(target, errors);
167+
}
168+
169+
@Override
170+
public void validate(Object target, Errors errors, Object... validationHints) {
171+
this.delegate.validate(target, errors, validationHints);
172+
}
173+
174+
@Override
175+
@SuppressWarnings("unchecked")
176+
public <T> T unwrap(Class<T> type) {
177+
if (type.isInstance(this.delegate)) {
178+
return (T) this.delegate;
179+
}
180+
return this.delegate.unwrap(type);
181+
}
182+
183+
}
184+
185+
}
186+
121187
@Configuration(proxyBeanMethods = false)
122188
static class NonManagedBeanConfig {
123189

0 commit comments

Comments
 (0)