Browse Source

Fix StackOverflowError in BindingReflectionHintsRegistrar

This commit fixes the cycle detection in
BindingReflectionHintsRegistrar.

See gh-28683
pull/28691/head
Sébastien Deleuze 3 years ago
parent
commit
61e9aa9f42
  1. 96
      spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java
  2. 20
      spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java

96
spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java

@ -22,7 +22,6 @@ import java.beans.Introspector; @@ -22,7 +22,6 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Consumer;
@ -34,7 +33,6 @@ import org.springframework.aot.hint.ExecutableHint; @@ -34,7 +33,6 @@ import org.springframework.aot.hint.ExecutableHint;
import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.core.KotlinDetector;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
@ -66,16 +64,9 @@ public class BindingReflectionHintsRegistrar { @@ -66,16 +64,9 @@ public class BindingReflectionHintsRegistrar {
* @param types the types to bind
*/
public void registerReflectionHints(ReflectionHints hints, Type... types) {
Set<Type> seen = new LinkedHashSet<>();
for (Type type : types) {
Set<Class<?>> referencedTypes = new LinkedHashSet<>();
collectReferencedTypes(new HashSet<>(), referencedTypes, type);
for (Class<?> referencedType : referencedTypes) {
hints.registerType(referencedType, builder -> {
if (shouldRegisterMembers(referencedType)) {
registerMembers(hints, referencedType, builder);
}
});
}
registerReflectionHints(hints, seen, type);
}
}
@ -88,40 +79,60 @@ public class BindingReflectionHintsRegistrar { @@ -88,40 +79,60 @@ public class BindingReflectionHintsRegistrar {
return !type.getCanonicalName().startsWith("java.");
}
private void registerMembers(ReflectionHints hints, Class<?> type, Builder builder) {
builder.withMembers(MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
try {
BeanInfo beanInfo = Introspector.getBeanInfo(type);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
Method writeMethod = propertyDescriptor.getWriteMethod();
if (writeMethod != null && writeMethod.getDeclaringClass() != Object.class) {
hints.registerMethod(writeMethod, INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(writeMethod, 0);
registerReflectionHints(hints, methodParameter.getGenericParameterType());
}
Method readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null && readMethod.getDeclaringClass() != Object.class) {
hints.registerMethod(readMethod, INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(readMethod, -1);
registerReflectionHints(hints, methodParameter.getGenericParameterType());
private void registerReflectionHints(ReflectionHints hints, Set<Type> seen, Type type) {
if (type instanceof Class<?> clazz) {
hints.registerType(clazz, builder -> {
if (seen.contains(type)) {
return;
}
}
String companionClassName = type.getCanonicalName() + KOTLIN_COMPANION_SUFFIX;
if (KotlinDetector.isKotlinType(type) && ClassUtils.isPresent(companionClassName, null)) {
Class<?> companionClass = ClassUtils.resolveClassName(companionClassName, null);
Method serializerMethod = ClassUtils.getMethodIfAvailable(companionClass, "serializer");
if (serializerMethod != null) {
hints.registerMethod(serializerMethod);
seen.add(type);
if (shouldRegisterMembers(clazz)) {
builder.withMembers(
MemberCategory.DECLARED_FIELDS,
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
try {
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
Method writeMethod = propertyDescriptor.getWriteMethod();
if (writeMethod != null && writeMethod.getDeclaringClass() != Object.class) {
hints.registerMethod(writeMethod, INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(writeMethod, 0);
Type methodParameterType = methodParameter.getGenericParameterType();
if (!seen.contains(methodParameterType)) {
registerReflectionHints(hints, seen, methodParameterType);
}
}
Method readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null && readMethod.getDeclaringClass() != Object.class) {
hints.registerMethod(readMethod, INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(readMethod, -1);
Type methodParameterType = methodParameter.getGenericParameterType();
if (!seen.contains(methodParameterType)) {
registerReflectionHints(hints, seen, methodParameterType);
}
}
}
String companionClassName = clazz.getCanonicalName() + KOTLIN_COMPANION_SUFFIX;
if (KotlinDetector.isKotlinType(clazz) && ClassUtils.isPresent(companionClassName, null)) {
Class<?> companionClass = ClassUtils.resolveClassName(companionClassName, null);
Method serializerMethod = ClassUtils.getMethodIfAvailable(companionClass, "serializer");
if (serializerMethod != null) {
hints.registerMethod(serializerMethod);
}
}
}
catch (IntrospectionException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring referenced type [" + clazz.getName() + "]: " + ex.getMessage());
}
}
}
}
}
catch (IntrospectionException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring referenced type [" + type.getName() + "]: " + ex.getMessage());
}
});
}
Set<Class<?>> referencedTypes = new LinkedHashSet<>();
collectReferencedTypes(seen, referencedTypes, type);
referencedTypes.forEach(referencedType -> registerReflectionHints(hints, seen, referencedType));
}
private void collectReferencedTypes(Set<Type> seen, Set<Class<?>> types, @Nullable Type type) {
@ -138,4 +149,5 @@ public class BindingReflectionHintsRegistrar { @@ -138,4 +149,5 @@ public class BindingReflectionHintsRegistrar {
}
}
}
}

20
spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java

@ -131,6 +131,15 @@ public class BindingReflectionHintsRegistrarTests { @@ -131,6 +131,15 @@ public class BindingReflectionHintsRegistrarTests {
});
}
@Test
void registerTypeForSerializationWithCycles() {
bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleClassWithCycles.class);
assertThat(this.hints.reflection().typeHints()).satisfiesExactlyInAnyOrder(
typeHint -> assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleClassWithCycles.class)),
typeHint -> assertThat(typeHint.getType()).isEqualTo(TypeReference.of(List.class)));
}
static class SampleEmptyClass {
}
@ -172,4 +181,15 @@ public class BindingReflectionHintsRegistrarTests { @@ -172,4 +181,15 @@ public class BindingReflectionHintsRegistrarTests {
}
}
static class SampleClassWithCycles {
public SampleClassWithCycles getSampleClassWithCycles() {
return null;
}
public List<SampleClassWithCycles> getSampleClassWithCyclesList() {
return null;
}
}
}

Loading…
Cancel
Save