Browse Source

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
pull/35924/head
Sébastien Deleuze 2 months ago
parent
commit
8647c44364
  1. 21
      spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java
  2. 2
      spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java
  3. 38
      spring-core/src/test/java/org/springframework/aot/hint/BindingReflectionHintsRegistrarTests.java

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

@ -20,6 +20,7 @@ import java.lang.annotation.Annotation; @@ -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 { @@ -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 { @@ -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
spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java

@ -23,6 +23,7 @@ import java.util.List; @@ -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; @@ -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
spring-core/src/test/java/org/springframework/aot/hint/BindingReflectionHintsRegistrarTests.java

@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; @@ -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 { @@ -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 { @@ -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) { }
}

Loading…
Cancel
Save