Skip to content

Commit

Permalink
Polish RegisterReflectionForBinding support
Browse files Browse the repository at this point in the history
Implementation and Javadoc refinements.

See spring-projectsgh-29279
  • Loading branch information
sdeleuze committed Oct 7, 2022
1 parent 1d05274 commit bbe285d
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 27 deletions.
Expand Up @@ -39,8 +39,8 @@
* Register the necessary reflection hints so that the specified type can be
* bound at runtime. Fields, constructors, properties and record components
* are registered, except for a set of types like those in the {@code java.}
* package where just the type is registered.
* Types are discovered transitively and generic type are registered as well.
* package where just the type is registered. Types are discovered transitively
* on properties and record components, and generic types are registered as well.
*
* @author Sebastien Deleuze
* @since 6.0
Expand All @@ -54,7 +54,7 @@ public class BindingReflectionHintsRegistrar {
/**
* Register the necessary reflection hints to bind the specified types.
* @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) {
Set<Type> seen = new LinkedHashSet<>();
Expand All @@ -73,15 +73,15 @@ protected boolean shouldRegisterMembers(Class<?> 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 (clazz.isPrimitive() || clazz == Object.class) {
return;
}
hints.registerType(clazz, typeHint -> {
if (seen.contains(type)) {
return;
}
seen.add(type);
if (shouldRegisterMembers(clazz)) {
if (clazz.isRecord()) {
typeHint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
Expand Down Expand Up @@ -114,17 +114,15 @@ private void registerReflectionHints(ReflectionHints hints, Set<Type> seen, Type
});
}
Set<Class<?>> referencedTypes = new LinkedHashSet<>();
collectReferencedTypes(seen, referencedTypes, type);
collectReferencedTypes(referencedTypes, ResolvableType.forType(type));
referencedTypes.forEach(referencedType -> registerReflectionHints(hints, seen, referencedType));
}

private void registerRecordHints(ReflectionHints hints, Set<Type> seen, Method method) {
hints.registerMethod(method, ExecutableMode.INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(method, -1);
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) {
Expand All @@ -133,9 +131,7 @@ private void registerPropertyHints(ReflectionHints hints, Set<Type> seen, @Nulla
hints.registerMethod(method, ExecutableMode.INVOKE);
MethodParameter methodParameter = MethodParameter.forExecutable(method, parameterIndex);
Type methodParameterType = methodParameter.getGenericParameterType();
if (!seen.contains(methodParameterType)) {
registerReflectionHints(hints, seen, methodParameterType);
}
registerReflectionHints(hints, seen, methodParameterType);
}
}

Expand All @@ -150,16 +146,12 @@ private void registerKotlinSerializationHints(ReflectionHints hints, Class<?> cl
}
}

private void collectReferencedTypes(Set<Type> seen, Set<Class<?>> types, Type type) {
if (seen.contains(type)) {
return;
}
ResolvableType resolvableType = ResolvableType.forType(type);
private void collectReferencedTypes(Set<Class<?>> types, ResolvableType resolvableType) {
Class<?> clazz = resolvableType.resolve();
if (clazz != null && !types.contains(clazz)) {
types.add(clazz);
for (ResolvableType genericResolvableType : resolvableType.getGenerics()) {
collectReferencedTypes(seen, types, genericResolvableType.getType());
collectReferencedTypes(types, genericResolvableType);
}
}
}
Expand Down
Expand Up @@ -26,8 +26,8 @@

/**
* Indicates that one or more {@link Class} reflection hints should be registered for
* data binding purpose (class, fields, properties, record components for the whole
* type hierarchy).
* data binding purpose (class, fields, properties, record components, including
* types transitively used on properties and record components).
*
* <p>Typically used to annotate the bean class or bean method where the reflection hint
* is needed.
Expand All @@ -42,16 +42,14 @@
public @interface RegisterReflectionForBinding {

/**
* Classes for which reflection hints should be registered to enable data binding
* (class, fields, properties, record components for the whole type hierarchy).
* Classes for which reflection hints should be registered.
* @see #classes()
*/
@AliasFor("classes")
Class<?>[] value() default {};

/**
* Classes for which reflection hints should be registered to enable data binding
* (class, fields, properties, record components for the whole type hierarchy).
* Classes for which reflection hints should be registered.
* @see #value()
*/
@AliasFor("value")
Expand Down
Expand Up @@ -25,7 +25,7 @@
/**
* A {@link ReflectiveProcessor} implementation that registers reflection hints
* 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
* @since 6.0
Expand Down

0 comments on commit bbe285d

Please sign in to comment.