Skip to content

Commit 3dc2237

Browse files
committed
Register runtime hints for Instant-to-Timestamp conversion
If an application depends on automatic type conversion from java.time.Instant to java.sql.Timestamp, the ObjectToObjectConverter performs the conversion based on convention, by using reflection to invoke Timestamp.from(Instant). However, when running in a native image a user needs to explicitly register runtime hints for that particular use of reflection. To assist users who are running their applications in a native image, this commit automatically registers the necessary runtime hints for Timestamp.from(Instant) so that users do not have to. See gh-35175 Closes gh-35156
1 parent 7900315 commit 3dc2237

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

spring-core/src/main/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHints.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.aot.hint.support;
1818

19+
import java.time.Instant;
1920
import java.time.LocalDate;
2021
import java.util.Collections;
2122
import java.util.List;
@@ -33,22 +34,30 @@
3334
* {@code org.springframework.core.convert.support.ObjectToObjectConverter}.
3435
*
3536
* @author Sebastien Deleuze
37+
* @author Sam Brannen
3638
* @since 6.0
3739
*/
3840
class ObjectToObjectConverterRuntimeHints implements RuntimeHintsRegistrar {
3941

4042
@Override
4143
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
4244
ReflectionHints reflectionHints = hints.reflection();
45+
4346
TypeReference sqlDateTypeReference = TypeReference.of("java.sql.Date");
4447
reflectionHints.registerTypeIfPresent(classLoader, sqlDateTypeReference.getName(), hint -> hint
4548
.withMethod("toLocalDate", Collections.emptyList(), ExecutableMode.INVOKE)
4649
.onReachableType(sqlDateTypeReference)
4750
.withMethod("valueOf", List.of(TypeReference.of(LocalDate.class)), ExecutableMode.INVOKE)
4851
.onReachableType(sqlDateTypeReference));
4952

53+
TypeReference sqlTimestampTypeReference = TypeReference.of("java.sql.Timestamp");
54+
reflectionHints.registerTypeIfPresent(classLoader, sqlTimestampTypeReference.getName(), hint -> hint
55+
.withMethod("from", List.of(TypeReference.of(Instant.class)), ExecutableMode.INVOKE)
56+
.onReachableType(sqlTimestampTypeReference));
57+
5058
reflectionHints.registerTypeIfPresent(classLoader, "org.springframework.http.HttpMethod",
5159
builder -> builder.withMethod("valueOf", List.of(TypeReference.of(String.class)), ExecutableMode.INVOKE));
60+
5261
reflectionHints.registerTypeIfPresent(classLoader, "java.net.URI", MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
5362
}
5463

spring-core/src/test/java/org/springframework/aot/hint/support/ObjectToObjectConverterRuntimeHintsTests.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,45 +17,52 @@
1717
package org.springframework.aot.hint.support;
1818

1919
import java.net.URI;
20+
import java.time.Instant;
2021
import java.time.LocalDate;
2122

2223
import org.junit.jupiter.api.BeforeEach;
2324
import org.junit.jupiter.api.Test;
2425

2526
import org.springframework.aot.hint.RuntimeHints;
2627
import org.springframework.aot.hint.RuntimeHintsRegistrar;
27-
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
2828
import org.springframework.core.io.support.SpringFactoriesLoader;
2929
import org.springframework.util.ClassUtils;
3030

3131
import static org.assertj.core.api.Assertions.assertThat;
32+
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
3233

3334
/**
3435
* Tests for {@link ObjectToObjectConverterRuntimeHints}.
3536
*
3637
* @author Sebastien Deleuze
38+
* @author Sam Brannen
3739
*/
3840
class ObjectToObjectConverterRuntimeHintsTests {
3941

40-
private RuntimeHints hints;
42+
private final RuntimeHints hints = new RuntimeHints();
43+
4144

4245
@BeforeEach
4346
void setup() {
44-
this.hints = new RuntimeHints();
4547
SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
46-
.load(RuntimeHintsRegistrar.class).forEach(registrar -> registrar
47-
.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));
48+
.load(RuntimeHintsRegistrar.class)
49+
.forEach(registrar -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));
4850
}
4951

5052
@Test
5153
void javaSqlDateHasHints() throws NoSuchMethodException {
52-
assertThat(RuntimeHintsPredicates.reflection().onMethod(java.sql.Date.class, "toLocalDate")).accepts(this.hints);
53-
assertThat(RuntimeHintsPredicates.reflection().onMethod(java.sql.Date.class.getMethod("valueOf", LocalDate.class))).accepts(this.hints);
54+
assertThat(reflection().onMethod(java.sql.Date.class, "toLocalDate")).accepts(this.hints);
55+
assertThat(reflection().onMethod(java.sql.Date.class.getMethod("valueOf", LocalDate.class))).accepts(this.hints);
56+
}
57+
58+
@Test // gh-35156
59+
void javaSqlTimestampHasHints() throws NoSuchMethodException {
60+
assertThat(reflection().onMethod(java.sql.Timestamp.class.getMethod("from", Instant.class))).accepts(this.hints);
5461
}
5562

5663
@Test
5764
void uriHasHints() throws NoSuchMethodException {
58-
assertThat(RuntimeHintsPredicates.reflection().onConstructor(URI.class.getConstructor(String.class))).accepts(this.hints);
65+
assertThat(reflection().onConstructor(URI.class.getConstructor(String.class))).accepts(this.hints);
5966
}
6067

6168
}

0 commit comments

Comments
 (0)