Skip to content

Commit 102cc72

Browse files
committedNov 24, 2024·
Merge branch '6.2.x'
2 parents 5e08a88 + 051f1da commit 102cc72

File tree

2 files changed

+39
-11
lines changed

2 files changed

+39
-11
lines changed
 

‎spring-context/src/main/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessor.java

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
1818

1919
import java.util.Collection;
2020
import java.util.HashSet;
21-
import java.util.List;
2221
import java.util.Map;
2322
import java.util.Optional;
2423
import java.util.Set;
@@ -107,18 +106,22 @@ public static BeanRegistrationAotContribution processAheadOfTime(RegisteredBean
107106
Set<Class<?>> validatedClasses = new HashSet<>();
108107
Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses = new HashSet<>();
109108

110-
processAheadOfTime(beanClass, validatedClasses, constraintValidatorClasses);
109+
processAheadOfTime(beanClass, new HashSet<>(), validatedClasses, constraintValidatorClasses);
111110

112111
if (!validatedClasses.isEmpty() || !constraintValidatorClasses.isEmpty()) {
113112
return new AotContribution(validatedClasses, constraintValidatorClasses);
114113
}
115114
return null;
116115
}
117116

118-
private static void processAheadOfTime(Class<?> clazz, Collection<Class<?>> validatedClasses,
119-
Collection<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
117+
private static void processAheadOfTime(Class<?> clazz, Set<Class<?>> visitedClasses, Set<Class<?>> validatedClasses,
118+
Set<Class<? extends ConstraintValidator<?, ?>>> constraintValidatorClasses) {
119+
120+
Assert.notNull(validator, "Validator cannot be null");
120121

121-
Assert.notNull(validator, "Validator can't be null");
122+
if (!visitedClasses.add(clazz)) {
123+
return;
124+
}
122125

123126
BeanDescriptor descriptor;
124127
try {
@@ -149,12 +152,12 @@ else if (ex instanceof TypeNotPresentException) {
149152

150153
ReflectionUtils.doWithFields(clazz, field -> {
151154
Class<?> type = field.getType();
152-
if (Iterable.class.isAssignableFrom(type) || List.class.isAssignableFrom(type) || Optional.class.isAssignableFrom(type)) {
155+
if (Iterable.class.isAssignableFrom(type) || Optional.class.isAssignableFrom(type)) {
153156
ResolvableType resolvableType = ResolvableType.forField(field);
154157
Class<?> genericType = resolvableType.getGeneric(0).toClass();
155158
if (shouldProcess(genericType)) {
156159
validatedClasses.add(clazz);
157-
processAheadOfTime(genericType, validatedClasses, constraintValidatorClasses);
160+
processAheadOfTime(genericType, visitedClasses, validatedClasses, constraintValidatorClasses);
158161
}
159162
}
160163
if (Map.class.isAssignableFrom(type)) {
@@ -163,11 +166,11 @@ else if (ex instanceof TypeNotPresentException) {
163166
Class<?> valueGenericType = resolvableType.getGeneric(1).toClass();
164167
if (shouldProcess(keyGenericType)) {
165168
validatedClasses.add(clazz);
166-
processAheadOfTime(keyGenericType, validatedClasses, constraintValidatorClasses);
169+
processAheadOfTime(keyGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
167170
}
168171
if (shouldProcess(valueGenericType)) {
169172
validatedClasses.add(clazz);
170-
processAheadOfTime(valueGenericType, validatedClasses, constraintValidatorClasses);
173+
processAheadOfTime(valueGenericType, visitedClasses, validatedClasses, constraintValidatorClasses);
171174
}
172175
}
173176
});

‎spring-context/src/test/java/org/springframework/validation/beanvalidation/BeanValidationBeanRegistrationAotProcessorTests.java

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,8 @@
2222
import java.lang.annotation.Target;
2323
import java.util.ArrayList;
2424
import java.util.List;
25+
import java.util.Map;
26+
import java.util.Optional;
2527

2628
import jakarta.validation.Constraint;
2729
import jakarta.validation.ConstraintValidator;
@@ -31,6 +33,8 @@
3133
import jakarta.validation.constraints.Pattern;
3234
import org.hibernate.validator.internal.constraintvalidators.bv.PatternValidator;
3335
import org.junit.jupiter.api.Test;
36+
import org.junit.jupiter.params.ParameterizedTest;
37+
import org.junit.jupiter.params.provider.ValueSource;
3438

3539
import org.springframework.aot.generate.GenerationContext;
3640
import org.springframework.aot.hint.MemberCategory;
@@ -121,6 +125,15 @@ void shouldProcessTransitiveGenericTypeLevelConstraint() {
121125
.withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.generationContext.getRuntimeHints());
122126
}
123127

128+
@ParameterizedTest // gh-33936
129+
@ValueSource(classes = {BeanWithRecursiveIterable.class, BeanWithRecursiveMap.class, BeanWithRecursiveOptional.class})
130+
void shouldProcessRecursiveGenericsWithoutInfiniteRecursion(Class<?> beanClass) {
131+
process(beanClass);
132+
assertThat(this.generationContext.getRuntimeHints().reflection().typeHints()).hasSize(1);
133+
assertThat(RuntimeHintsPredicates.reflection().onType(beanClass)
134+
.withMemberCategory(MemberCategory.DECLARED_FIELDS)).accepts(this.generationContext.getRuntimeHints());
135+
}
136+
124137
private void process(Class<?> beanClass) {
125138
BeanRegistrationAotContribution contribution = createContribution(beanClass);
126139
if (contribution != null) {
@@ -244,4 +257,16 @@ public void setExclude(List<Exclude> exclude) {
244257
}
245258
}
246259

260+
static class BeanWithRecursiveIterable {
261+
Iterable<BeanWithRecursiveIterable> iterable;
262+
}
263+
264+
static class BeanWithRecursiveMap {
265+
Map<BeanWithRecursiveMap, BeanWithRecursiveMap> map;
266+
}
267+
268+
static class BeanWithRecursiveOptional {
269+
Optional<BeanWithRecursiveOptional> optional;
270+
}
271+
247272
}

0 commit comments

Comments
 (0)
Please sign in to comment.