1818
1919import java .util .HashMap ;
2020
21+ import jakarta .validation .Validator ;
2122import jakarta .validation .constraints .Min ;
23+ import org .hibernate .validator .HibernateValidator ;
2224import org .junit .jupiter .api .Test ;
2325
2426import org .springframework .boot .test .context .FilteredClassLoader ;
2729import org .springframework .context .annotation .Bean ;
2830import org .springframework .context .annotation .Configuration ;
2931import org .springframework .core .io .ClassPathResource ;
32+ import org .springframework .validation .Errors ;
3033import org .springframework .validation .MapBindingResult ;
34+ import org .springframework .validation .SmartValidator ;
3135import org .springframework .validation .beanvalidation .LocalValidatorFactoryBean ;
3236
3337import static org .assertj .core .api .Assertions .assertThat ;
38+ import static org .assertj .core .api .Assertions .assertThatRuntimeException ;
3439import static org .mockito .ArgumentMatchers .any ;
3540import static org .mockito .BDDMockito .then ;
3641import static org .mockito .Mockito .mock ;
@@ -91,6 +96,30 @@ void wrapperWhenValidationProviderNotPresentShouldNotThrowException() {
9196 .run ((context ) -> ValidatorAdapter .get (context , null ));
9297 }
9398
99+ @ Test
100+ void unwrapToJakartaValidatorShouldReturnJakartaValidator () {
101+ this .contextRunner .withUserConfiguration (LocalValidatorFactoryBeanConfig .class ).run ((context ) -> {
102+ ValidatorAdapter wrapper = context .getBean (ValidatorAdapter .class );
103+ assertThat (wrapper .unwrap (Validator .class )).isInstanceOf (Validator .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 ));
120+ });
121+ }
122+
94123 @ Configuration (proxyBeanMethods = false )
95124 static class LocalValidatorFactoryBeanConfig {
96125
@@ -106,6 +135,55 @@ ValidatorAdapter wrapper(LocalValidatorFactoryBean validator) {
106135
107136 }
108137
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+
109187 @ Configuration (proxyBeanMethods = false )
110188 static class NonManagedBeanConfig {
111189
0 commit comments