diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java b/spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java index e0996284cb7..536582464be 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java @@ -16,6 +16,7 @@ package org.springframework.aot.hint.support; +import java.time.Instant; import java.time.LocalDate; import java.util.Collections; import java.util.List; @@ -33,6 +34,7 @@ import org.springframework.lang.Nullable; * {@code org.springframework.core.convert.support.ObjectToObjectConverter}. * * @author Sebastien Deleuze + * @author Sam Brannen * @since 6.0 */ class ObjectToObjectConverterRuntimeHints implements RuntimeHintsRegistrar { @@ -40,6 +42,7 @@ class ObjectToObjectConverterRuntimeHints implements RuntimeHintsRegistrar { @Override public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { ReflectionHints reflectionHints = hints.reflection(); + TypeReference sqlDateTypeReference = TypeReference.of("java.sql.Date"); reflectionHints.registerTypeIfPresent(classLoader, sqlDateTypeReference.getName(), hint -> hint .withMethod("toLocalDate", Collections.emptyList(), ExecutableMode.INVOKE) @@ -47,8 +50,14 @@ class ObjectToObjectConverterRuntimeHints implements RuntimeHintsRegistrar { .withMethod("valueOf", List.of(TypeReference.of(LocalDate.class)), ExecutableMode.INVOKE) .onReachableType(sqlDateTypeReference)); + TypeReference sqlTimestampTypeReference = TypeReference.of("java.sql.Timestamp"); + reflectionHints.registerTypeIfPresent(classLoader, sqlTimestampTypeReference.getName(), hint -> hint + .withMethod("from", List.of(TypeReference.of(Instant.class)), ExecutableMode.INVOKE) + .onReachableType(sqlTimestampTypeReference)); + reflectionHints.registerTypeIfPresent(classLoader, "org.springframework.http.HttpMethod", builder -> builder.withMethod("valueOf", List.of(TypeReference.of(String.class)), ExecutableMode.INVOKE)); + reflectionHints.registerTypeIfPresent(classLoader, "java.net.URI", MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHintsTests.java index 8c480a8a717..a537f3ecbf3 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHintsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHintsTests.java @@ -17,6 +17,7 @@ package org.springframework.aot.hint.support; import java.net.URI; +import java.time.Instant; import java.time.LocalDate; import org.junit.jupiter.api.BeforeEach; @@ -24,38 +25,44 @@ import org.junit.jupiter.api.Test; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.util.ClassUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; /** * Tests for {@link ObjectToObjectConverterRuntimeHints}. * * @author Sebastien Deleuze + * @author Sam Brannen */ class ObjectToObjectConverterRuntimeHintsTests { - private RuntimeHints hints; + private final RuntimeHints hints = new RuntimeHints(); + @BeforeEach void setup() { - this.hints = new RuntimeHints(); SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories") - .load(RuntimeHintsRegistrar.class).forEach(registrar -> registrar - .registerHints(this.hints, ClassUtils.getDefaultClassLoader())); + .load(RuntimeHintsRegistrar.class) + .forEach(registrar -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader())); } @Test void javaSqlDateHasHints() throws NoSuchMethodException { - assertThat(RuntimeHintsPredicates.reflection().onMethod(java.sql.Date.class, "toLocalDate")).accepts(this.hints); - assertThat(RuntimeHintsPredicates.reflection().onMethod(java.sql.Date.class.getMethod("valueOf", LocalDate.class))).accepts(this.hints); + assertThat(reflection().onMethod(java.sql.Date.class, "toLocalDate")).accepts(this.hints); + assertThat(reflection().onMethod(java.sql.Date.class.getMethod("valueOf", LocalDate.class))).accepts(this.hints); + } + + @Test // gh-35156 + void javaSqlTimestampHasHints() throws NoSuchMethodException { + assertThat(reflection().onMethod(java.sql.Timestamp.class.getMethod("from", Instant.class))).accepts(this.hints); } @Test void uriHasHints() throws NoSuchMethodException { - assertThat(RuntimeHintsPredicates.reflection().onConstructor(URI.class.getConstructor(String.class))).accepts(this.hints); + assertThat(reflection().onConstructor(URI.class.getConstructor(String.class))).accepts(this.hints); } }