Polish RegisterReflectionForBinding support

Implementation and Javadoc refinements.

See gh-29279
This commit is contained in:
Sébastien Deleuze 2022-10-07 11:29:34 +02:00
parent 1d052742d1
commit bbe285dd42
3 changed files with 17 additions and 27 deletions

View File

@ -39,8 +39,8 @@ import org.springframework.util.ClassUtils;
* Register the necessary reflection hints so that the specified type can be * Register the necessary reflection hints so that the specified type can be
* bound at runtime. Fields, constructors, properties and record components * bound at runtime. Fields, constructors, properties and record components
* are registered, except for a set of types like those in the {@code java.} * are registered, except for a set of types like those in the {@code java.}
* package where just the type is registered. * package where just the type is registered. Types are discovered transitively
* Types are discovered transitively and generic type are registered as well. * on properties and record components, and generic types are registered as well.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 6.0 * @since 6.0
@ -54,7 +54,7 @@ public class BindingReflectionHintsRegistrar {
/** /**
* Register the necessary reflection hints to bind the specified types. * Register the necessary reflection hints to bind the specified types.
* @param hints the hints instance to use * @param hints the hints instance to use
* @param types the types to bind * @param types the types to register
*/ */
public void registerReflectionHints(ReflectionHints hints, Type... types) { public void registerReflectionHints(ReflectionHints hints, Type... types) {
Set<Type> seen = new LinkedHashSet<>(); Set<Type> seen = new LinkedHashSet<>();
@ -73,15 +73,15 @@ public class BindingReflectionHintsRegistrar {
} }
private void registerReflectionHints(ReflectionHints hints, Set<Type> seen, Type type) { private void registerReflectionHints(ReflectionHints hints, Set<Type> seen, Type type) {
if (seen.contains(type)) {
return;
}
seen.add(type);
if (type instanceof Class<?> clazz) { if (type instanceof Class<?> clazz) {
if (clazz.isPrimitive() || clazz == Object.class) { if (clazz.isPrimitive() || clazz == Object.class) {
return; return;
} }
hints.registerType(clazz, typeHint -> { hints.registerType(clazz, typeHint -> {
if (seen.contains(type)) {
return;
}
seen.add(type);
if (shouldRegisterMembers(clazz)) { if (shouldRegisterMembers(clazz)) {
if (clazz.isRecord()) { if (clazz.isRecord()) {
typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
@ -114,7 +114,7 @@ public class BindingReflectionHintsRegistrar {
}); });
} }
Set<Class<?>> referencedTypes = new LinkedHashSet<>(); Set<Class<?>> referencedTypes = new LinkedHashSet<>();
collectReferencedTypes(seen, referencedTypes, type); collectReferencedTypes(referencedTypes, ResolvableType.forType(type));
referencedTypes.forEach(referencedType -> registerReflectionHints(hints, seen, referencedType)); referencedTypes.forEach(referencedType -> registerReflectionHints(hints, seen, referencedType));
} }
@ -122,10 +122,8 @@ public class BindingReflectionHintsRegistrar {
hints.registerMethod(method, ExecutableMode.INVOKE); hints.registerMethod(method, ExecutableMode.INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(method, -1); MethodParameter methodParameter = MethodParameter.forExecutable(method, -1);
Type methodParameterType = methodParameter.getGenericParameterType(); Type methodParameterType = methodParameter.getGenericParameterType();
if (!seen.contains(methodParameterType)) {
registerReflectionHints(hints, seen, methodParameterType); registerReflectionHints(hints, seen, methodParameterType);
} }
}
private void registerPropertyHints(ReflectionHints hints, Set<Type> seen, @Nullable Method method, int parameterIndex) { private void registerPropertyHints(ReflectionHints hints, Set<Type> seen, @Nullable Method method, int parameterIndex) {
if (method != null && method.getDeclaringClass() != Object.class if (method != null && method.getDeclaringClass() != Object.class
@ -133,11 +131,9 @@ public class BindingReflectionHintsRegistrar {
hints.registerMethod(method, ExecutableMode.INVOKE); hints.registerMethod(method, ExecutableMode.INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(method, parameterIndex); MethodParameter methodParameter = MethodParameter.forExecutable(method, parameterIndex);
Type methodParameterType = methodParameter.getGenericParameterType(); Type methodParameterType = methodParameter.getGenericParameterType();
if (!seen.contains(methodParameterType)) {
registerReflectionHints(hints, seen, methodParameterType); registerReflectionHints(hints, seen, methodParameterType);
} }
} }
}
private void registerKotlinSerializationHints(ReflectionHints hints, Class<?> clazz) { private void registerKotlinSerializationHints(ReflectionHints hints, Class<?> clazz) {
String companionClassName = clazz.getCanonicalName() + KOTLIN_COMPANION_SUFFIX; String companionClassName = clazz.getCanonicalName() + KOTLIN_COMPANION_SUFFIX;
@ -150,16 +146,12 @@ public class BindingReflectionHintsRegistrar {
} }
} }
private void collectReferencedTypes(Set<Type> seen, Set<Class<?>> types, Type type) { private void collectReferencedTypes(Set<Class<?>> types, ResolvableType resolvableType) {
if (seen.contains(type)) {
return;
}
ResolvableType resolvableType = ResolvableType.forType(type);
Class<?> clazz = resolvableType.resolve(); Class<?> clazz = resolvableType.resolve();
if (clazz != null && !types.contains(clazz)) { if (clazz != null && !types.contains(clazz)) {
types.add(clazz); types.add(clazz);
for (ResolvableType genericResolvableType : resolvableType.getGenerics()) { for (ResolvableType genericResolvableType : resolvableType.getGenerics()) {
collectReferencedTypes(seen, types, genericResolvableType.getType()); collectReferencedTypes(types, genericResolvableType);
} }
} }
} }

View File

@ -26,8 +26,8 @@ import org.springframework.core.annotation.AliasFor;
/** /**
* Indicates that one or more {@link Class} reflection hints should be registered for * Indicates that one or more {@link Class} reflection hints should be registered for
* data binding purpose (class, fields, properties, record components for the whole * data binding purpose (class, fields, properties, record components, including
* type hierarchy). * types transitively used on properties and record components).
* *
* <p>Typically used to annotate the bean class or bean method where the reflection hint * <p>Typically used to annotate the bean class or bean method where the reflection hint
* is needed. * is needed.
@ -42,16 +42,14 @@ import org.springframework.core.annotation.AliasFor;
public @interface RegisterReflectionForBinding { public @interface RegisterReflectionForBinding {
/** /**
* Classes for which reflection hints should be registered to enable data binding * Classes for which reflection hints should be registered.
* (class, fields, properties, record components for the whole type hierarchy).
* @see #classes() * @see #classes()
*/ */
@AliasFor("classes") @AliasFor("classes")
Class<?>[] value() default {}; Class<?>[] value() default {};
/** /**
* Classes for which reflection hints should be registered to enable data binding * Classes for which reflection hints should be registered.
* (class, fields, properties, record components for the whole type hierarchy).
* @see #value() * @see #value()
*/ */
@AliasFor("value") @AliasFor("value")

View File

@ -25,7 +25,7 @@ import org.springframework.core.annotation.AnnotationUtils;
/** /**
* A {@link ReflectiveProcessor} implementation that registers reflection hints * A {@link ReflectiveProcessor} implementation that registers reflection hints
* for data binding purpose (class, constructors, fields, properties, record * for data binding purpose (class, constructors, fields, properties, record
* components for the whole type hierarchy). * components, including types transitively used on properties and record components).
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 6.0 * @since 6.0