This commit is contained in:
Stephane Nicoll 2022-06-14 14:11:21 +02:00
parent d6d4b98780
commit 77ad4a1428
3 changed files with 48 additions and 34 deletions

View File

@ -34,14 +34,16 @@ import org.springframework.aot.hint.ExecutableHint;
import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ExecutableMode;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.TypeHint.Builder;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
/** /**
* Register the necessary reflection hints so that the specified type can be bound/serialized at runtime. * Register the necessary reflection hints so that the specified type can be
* Fields, constructors and property methods are registered, except for a set of types like those in * bound at runtime. Fields, constructors and property methods are registered,
* {@code java.} package where just the type is registered. Types are discovered transitively and * except for a set of types like those in the {@code java.} package where just
* generic types are registered as well. * the type is registered. Types are discovered transitively and generic types
* are registered as well.
* *
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 6.0 * @since 6.0
@ -53,38 +55,22 @@ public class BindingReflectionHintsRegistrar {
private static final Consumer<ExecutableHint.Builder> INVOKE = builder -> builder private static final Consumer<ExecutableHint.Builder> INVOKE = builder -> builder
.withMode(ExecutableMode.INVOKE); .withMode(ExecutableMode.INVOKE);
/**
* Register the necessary reflection hints to bind the specified types.
* @param hints the hints instance to use
* @param types the types to bind
*/
public void registerReflectionHints(ReflectionHints hints, Type... types) { public void registerReflectionHints(ReflectionHints hints, Type... types) {
for (Type type : types) { for (Type type : types) {
Set<Class<?>> referencedTypes = new LinkedHashSet<>(); Set<Class<?>> referencedTypes = new LinkedHashSet<>();
collectReferencedTypes(new HashSet<>(), referencedTypes, type); collectReferencedTypes(new HashSet<>(), referencedTypes, type);
referencedTypes.forEach(referencedType -> hints.registerType(referencedType, builder -> { for (Class<?> referencedType : referencedTypes) {
if (shouldRegisterMembers(referencedType)) { hints.registerType(referencedType, builder -> {
builder.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); if (shouldRegisterMembers(referencedType)) {
try { registerMembers(hints, referencedType, builder);
BeanInfo beanInfo = Introspector.getBeanInfo(referencedType);
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());
}
}
} }
catch (IntrospectionException ex) { });
if (logger.isDebugEnabled()) { }
logger.debug("Ignoring referenced type [" + referencedType.getName() + "]: " + ex.getMessage());
}
}
}
}));
} }
} }
@ -97,13 +83,41 @@ public class BindingReflectionHintsRegistrar {
return !type.getCanonicalName().startsWith("java."); 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());
}
}
}
catch (IntrospectionException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring referenced type [" + type.getName() + "]: " + ex.getMessage());
}
}
}
private void collectReferencedTypes(Set<Type> seen, Set<Class<?>> types, Type type) { private void collectReferencedTypes(Set<Type> seen, Set<Class<?>> types, Type type) {
if (seen.contains(type)) { if (seen.contains(type)) {
return; return;
} }
seen.add(type); seen.add(type);
ResolvableType resolvableType = ResolvableType.forType(type); ResolvableType resolvableType = ResolvableType.forType(type);
Class<?> clazz = resolvableType.resolve(); Class<?> clazz = resolvableType.resolve();
if (clazz != null) { if (clazz != null) {
types.add(clazz); types.add(clazz);
for (ResolvableType genericResolvableType : resolvableType.getGenerics()) { for (ResolvableType genericResolvableType : resolvableType.getGenerics()) {

View File

@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat;
public class BindingReflectionHintsRegistrarTests { public class BindingReflectionHintsRegistrarTests {
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
private final RuntimeHints hints = new RuntimeHints(); private final RuntimeHints hints = new RuntimeHints();
@Test @Test

View File

@ -34,14 +34,13 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
* with {@link ResponseBody} and parameters annotated with {@link RequestBody} * with {@link ResponseBody} and parameters annotated with {@link RequestBody}
* which are serialized as well. * which are serialized as well.
* *
*
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @since 6.0 * @since 6.0
*/ */
class RequestMappingReflectiveProcessor implements ReflectiveProcessor { class RequestMappingReflectiveProcessor implements ReflectiveProcessor {
private BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar(); private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
@Override @Override
public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) { public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {