mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-10-02 04:09:01 +00:00
Add dynamic ObjectToObjectConverter hints
This commit refines BindingReflectionHintsRegistrar with additional dynamic hints for application-defined types, main core Java conversion ones being already covered by ObjectToObjectConverterRuntimeHints. Closes gh-35847
This commit is contained in:
+21
@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashSet;
|
||||
@@ -116,6 +117,7 @@ public class BindingReflectionHintsRegistrar {
|
||||
if (JACKSON_ANNOTATION_PRESENT) {
|
||||
registerJacksonHints(hints, clazz);
|
||||
}
|
||||
registerObjectToObjectConverterHints(hints, clazz);
|
||||
}
|
||||
if (KotlinDetector.isKotlinType(clazz)) {
|
||||
KotlinDelegate.registerComponentHints(hints, clazz);
|
||||
@@ -156,6 +158,25 @@ public class BindingReflectionHintsRegistrar {
|
||||
}
|
||||
}
|
||||
|
||||
// See also the static hints registered by ObjectToObjectConverterRuntimeHints
|
||||
private void registerObjectToObjectConverterHints(ReflectionHints hints, Class<?> clazz) {
|
||||
for (Method method : clazz.getMethods()) {
|
||||
String name = method.getName();
|
||||
boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
if (isStatic && (clazz != String.class) && areRelatedTypes(method.getReturnType(), clazz) &&
|
||||
(name.equals("valueOf") || name.equals("of") || name.equals("from"))) {
|
||||
hints.registerMethod(method, ExecutableMode.INVOKE);
|
||||
}
|
||||
if (!isStatic && (method.getReturnType() != String.class) && name.equals("to" + method.getReturnType().getSimpleName())) {
|
||||
hints.registerMethod(method, ExecutableMode.INVOKE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean areRelatedTypes(Class<?> type1, Class<?> type2) {
|
||||
return (ClassUtils.isAssignable(type1, type2) || ClassUtils.isAssignable(type2, type1));
|
||||
}
|
||||
|
||||
private void collectReferencedTypes(Set<Class<?>> types, ResolvableType resolvableType) {
|
||||
Class<?> clazz = resolvableType.resolve();
|
||||
if (clazz != null && !types.contains(clazz)) {
|
||||
|
||||
+2
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
|
||||
import org.springframework.aot.hint.ExecutableMode;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
@@ -33,6 +34,7 @@ import org.springframework.aot.hint.TypeReference;
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} to register hints for popular conventions in
|
||||
* {@code org.springframework.core.convert.support.ObjectToObjectConverter}.
|
||||
* Some dynamic hints registered by {@link BindingReflectionHintsRegistrar}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @author Sam Brannen
|
||||
|
||||
+38
@@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
|
||||
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.predicate.ReflectionHintsPredicates;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
@@ -301,6 +302,16 @@ class BindingReflectionHintsRegistrarTests {
|
||||
.accepts(this.hints);
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerTypeForObjectToObjectConverter() {
|
||||
bindingRegistrar.registerReflectionHints(this.hints.reflection(), Source.class);
|
||||
ReflectionHintsPredicates reflection = RuntimeHintsPredicates.reflection();
|
||||
assertThat(reflection.onMethodInvocation(Source.class, "valueOf")).accepts(this.hints);
|
||||
assertThat(reflection.onMethodInvocation(Source.class, "of")).accepts(this.hints);
|
||||
assertThat(reflection.onMethodInvocation(Source.class, "from")).accepts(this.hints);
|
||||
assertThat(reflection.onMethodInvocation(Source.class, "toData")).accepts(this.hints);
|
||||
}
|
||||
|
||||
|
||||
static class SampleEmptyClass {
|
||||
}
|
||||
@@ -460,4 +471,31 @@ class BindingReflectionHintsRegistrarTests {
|
||||
}
|
||||
}
|
||||
|
||||
static class Source {
|
||||
|
||||
private final String value;
|
||||
|
||||
private Source(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static Source valueOf(String value) {
|
||||
return new Source(value);
|
||||
}
|
||||
|
||||
public static Source of(String value) {
|
||||
return new Source(value);
|
||||
}
|
||||
|
||||
public static Source from(String value) {
|
||||
return new Source(value);
|
||||
}
|
||||
|
||||
public Data toData() {
|
||||
return new Data(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
record Data(String value) { }
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user