Skip to content

Commit 8073259

Browse files
committed
Add class hints for Jackson annotations on fields and methods
Before this commit, only class level annotations were processed. Closes gh-30208
1 parent d451d6a commit 8073259

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

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

+11-5
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,17 @@ private void registerJacksonHints(ReflectionHints hints, Class<?> clazz) {
169169
if (sourceField != null) {
170170
hints.registerField(sourceField);
171171
}
172+
registerHintsForClassAttributes(hints, annotation);
172173
}));
173174
ReflectionUtils.doWithMethods(clazz, method ->
174175
forEachJacksonAnnotation(method, annotation -> {
175176
Method sourceMethod = (Method) annotation.getSource();
176177
if (sourceMethod != null) {
177178
hints.registerMethod(sourceMethod, ExecutableMode.INVOKE);
178179
}
180+
registerHintsForClassAttributes(hints, annotation);
179181
}));
180-
forEachJacksonAnnotation(clazz, annotation -> annotation.getRoot().asMap().values().forEach(value -> {
181-
if (value instanceof Class<?> classValue) {
182-
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
183-
}
184-
}));
182+
forEachJacksonAnnotation(clazz, annotation -> registerHintsForClassAttributes(hints, annotation));
185183
}
186184

187185
private void forEachJacksonAnnotation(AnnotatedElement element, Consumer<MergedAnnotation<Annotation>> action) {
@@ -192,6 +190,14 @@ private void forEachJacksonAnnotation(AnnotatedElement element, Consumer<MergedA
192190
.forEach(action::accept);
193191
}
194192

193+
private void registerHintsForClassAttributes(ReflectionHints hints, MergedAnnotation<Annotation> annotation) {
194+
annotation.getRoot().asMap().values().forEach(value -> {
195+
if (value instanceof Class<?> classValue && value != Void.class) {
196+
hints.registerType(classValue, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
197+
}
198+
});
199+
}
200+
195201
/**
196202
* Inner class to avoid a hard dependency on Kotlin at runtime.
197203
*/

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

+41
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,21 @@
1616

1717
package org.springframework.aot.hint;
1818

19+
import java.io.IOException;
1920
import java.lang.reflect.Type;
21+
import java.time.LocalDate;
2022
import java.util.List;
2123
import java.util.Set;
2224

2325
import com.fasterxml.jackson.annotation.JsonProperty;
26+
import com.fasterxml.jackson.core.JacksonException;
27+
import com.fasterxml.jackson.core.JsonParser;
28+
import com.fasterxml.jackson.databind.DeserializationContext;
2429
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
2530
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2631
import com.fasterxml.jackson.databind.annotation.JsonNaming;
2732
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
33+
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
2834
import org.junit.jupiter.api.Test;
2935

3036
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
@@ -265,6 +271,15 @@ void registerTypeForJacksonCustomStrategy() {
265271
.accepts(this.hints);
266272
}
267273

274+
@Test
275+
void registerTypeForAnnotationOnMethodAndField() {
276+
bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleClassWithJsonProperty.class);
277+
assertThat(RuntimeHintsPredicates.reflection().onType(CustomDeserializer1.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
278+
.accepts(this.hints);
279+
assertThat(RuntimeHintsPredicates.reflection().onType(CustomDeserializer2.class).withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS))
280+
.accepts(this.hints);
281+
}
282+
268283

269284
static class SampleEmptyClass {
270285
}
@@ -359,9 +374,11 @@ public String getNameProperty() {
359374
static class SampleClassWithJsonProperty {
360375

361376
@JsonProperty
377+
@JsonDeserialize(using = CustomDeserializer1.class)
362378
private String privateField = "";
363379

364380
@JsonProperty
381+
@JsonDeserialize(using = CustomDeserializer2.class)
365382
String packagePrivateMethod() {
366383
return "";
367384
}
@@ -393,4 +410,28 @@ public SampleRecordWithJacksonCustomStrategy build() {
393410

394411
}
395412

413+
static class CustomDeserializer1 extends StdDeserializer<LocalDate> {
414+
415+
public CustomDeserializer1() {
416+
super(CustomDeserializer1.class);
417+
}
418+
419+
@Override
420+
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
421+
return null;
422+
}
423+
}
424+
425+
static class CustomDeserializer2 extends StdDeserializer<LocalDate> {
426+
427+
public CustomDeserializer2() {
428+
super(CustomDeserializer2.class);
429+
}
430+
431+
@Override
432+
public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
433+
return null;
434+
}
435+
}
436+
396437
}

0 commit comments

Comments
 (0)