Add support for Jackson annotations in BindingReflectionHintsRegistrar
This commits registers reflection hints on field and methods where Jackson annotations are detected. Closes gh-29426
This commit is contained in:
parent
2878ade980
commit
60c9f2f72f
|
@ -84,6 +84,8 @@ dependencies {
|
||||||
testImplementation("io.projectreactor.tools:blockhound")
|
testImplementation("io.projectreactor.tools:blockhound")
|
||||||
testImplementation("org.skyscreamer:jsonassert")
|
testImplementation("org.skyscreamer:jsonassert")
|
||||||
testImplementation("com.squareup.okhttp3:mockwebserver")
|
testImplementation("com.squareup.okhttp3:mockwebserver")
|
||||||
|
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
||||||
|
testImplementation("com.fasterxml.jackson.core:jackson-annotations")
|
||||||
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
testFixturesImplementation("com.google.code.findbugs:jsr305")
|
||||||
testFixturesImplementation("org.junit.platform:junit-platform-launcher")
|
testFixturesImplementation("org.junit.platform:junit-platform-launcher")
|
||||||
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
|
testFixturesImplementation("org.junit.jupiter:junit-jupiter-api")
|
||||||
|
@ -91,7 +93,6 @@ dependencies {
|
||||||
testFixturesImplementation("org.assertj:assertj-core")
|
testFixturesImplementation("org.assertj:assertj-core")
|
||||||
testFixturesImplementation("org.xmlunit:xmlunit-assertj")
|
testFixturesImplementation("org.xmlunit:xmlunit-assertj")
|
||||||
testFixturesImplementation("io.projectreactor:reactor-test")
|
testFixturesImplementation("io.projectreactor:reactor-test")
|
||||||
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.aot.hint;
|
package org.springframework.aot.hint;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.RecordComponent;
|
import java.lang.reflect.RecordComponent;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
@ -28,8 +29,11 @@ import kotlin.reflect.KClass;
|
||||||
import org.springframework.core.KotlinDetector;
|
import org.springframework.core.KotlinDetector;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the necessary reflection hints so that the specified type can be
|
* Register the necessary reflection hints so that the specified type can be
|
||||||
|
@ -45,6 +49,11 @@ public class BindingReflectionHintsRegistrar {
|
||||||
|
|
||||||
private static final String KOTLIN_COMPANION_SUFFIX = "$Companion";
|
private static final String KOTLIN_COMPANION_SUFFIX = "$Companion";
|
||||||
|
|
||||||
|
private static final String JACKSON_ANNOTATION = "com.fasterxml.jackson.annotation.JacksonAnnotation";
|
||||||
|
|
||||||
|
private static final boolean jacksonAnnotationPresent = ClassUtils.isPresent(JACKSON_ANNOTATION,
|
||||||
|
BindingReflectionHintsRegistrar.class.getClassLoader());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the necessary reflection hints to bind the specified types.
|
* Register the necessary reflection hints to bind the specified types.
|
||||||
* @param hints the hints instance to use
|
* @param hints the hints instance to use
|
||||||
|
@ -97,6 +106,9 @@ public class BindingReflectionHintsRegistrar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (jacksonAnnotationPresent) {
|
||||||
|
registerJacksonHints(hints, clazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (KotlinDetector.isKotlinType(clazz)) {
|
if (KotlinDetector.isKotlinType(clazz)) {
|
||||||
KotlinDelegate.registerComponentHints(hints, clazz);
|
KotlinDelegate.registerComponentHints(hints, clazz);
|
||||||
|
@ -147,6 +159,31 @@ public class BindingReflectionHintsRegistrar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerJacksonHints(ReflectionHints hints, Class<?> clazz) {
|
||||||
|
ReflectionUtils.doWithFields(clazz, field ->
|
||||||
|
MergedAnnotations
|
||||||
|
.from(field, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
|
||||||
|
.stream(JACKSON_ANNOTATION)
|
||||||
|
.filter(MergedAnnotation::isMetaPresent)
|
||||||
|
.forEach(annotation -> {
|
||||||
|
Field sourceField = (Field) annotation.getSource();
|
||||||
|
if (sourceField != null) {
|
||||||
|
hints.registerField(sourceField);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
ReflectionUtils.doWithMethods(clazz, method ->
|
||||||
|
MergedAnnotations
|
||||||
|
.from(method, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
|
||||||
|
.stream(JACKSON_ANNOTATION)
|
||||||
|
.filter(MergedAnnotation::isMetaPresent)
|
||||||
|
.forEach(annotation -> {
|
||||||
|
Method sourceMethod = (Method) annotation.getSource();
|
||||||
|
if (sourceMethod != null) {
|
||||||
|
hints.registerMethod(sourceMethod, ExecutableMode.INVOKE);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,8 +20,10 @@ import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
@ -219,6 +221,24 @@ public class BindingReflectionHintsRegistrarTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerTypeForJacksonAnnotations() {
|
||||||
|
bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleClassWithJsonProperty.class);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection().onField(SampleClassWithJsonProperty.class, "privateField"))
|
||||||
|
.accepts(this.hints);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection().onMethod(SampleClassWithJsonProperty.class, "packagePrivateMethod").invoke())
|
||||||
|
.accepts(this.hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void registerTypeForInheritedJacksonAnnotations() {
|
||||||
|
bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleClassWithInheritedJsonProperty.class);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection().onField(SampleClassWithJsonProperty.class, "privateField"))
|
||||||
|
.accepts(this.hints);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection().onMethod(SampleClassWithJsonProperty.class, "packagePrivateMethod").invoke())
|
||||||
|
.accepts(this.hints);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static class SampleEmptyClass {
|
static class SampleEmptyClass {
|
||||||
}
|
}
|
||||||
|
@ -291,7 +311,7 @@ public class BindingReflectionHintsRegistrarTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SampleClassC {
|
static class SampleClassC {
|
||||||
public String getString() {
|
public String getString() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -303,4 +323,17 @@ public class BindingReflectionHintsRegistrarTests {
|
||||||
|
|
||||||
record SampleRecord(String name) {}
|
record SampleRecord(String name) {}
|
||||||
|
|
||||||
|
static class SampleClassWithJsonProperty {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String privateField = "";
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
String packagePrivateMethod() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SampleClassWithInheritedJsonProperty extends SampleClassWithJsonProperty {}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue