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
This commit is contained in:
parent
7900315f23
commit
3dc22379a0
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue