Skip to content

Commit 2f94713

Browse files
committed
Make sure that annotation hierarchy is registered
This commit improves registerAnnotation to also registers the meta annotation sources, if any. Without them, the annotation could not be fully resolved. See gh-28497
1 parent 16a4a9a commit 2f94713

File tree

2 files changed

+62
-19
lines changed

2 files changed

+62
-19
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ public abstract class RuntimeHintsUtils {
4848
* @see SynthesizedAnnotation
4949
*/
5050
public static void registerAnnotation(RuntimeHints hints, MergedAnnotation<?> annotation) {
51-
registerAnnotation(hints, annotation.getType(),
52-
annotation.synthesize() instanceof SynthesizedAnnotation);
53-
}
54-
55-
private static void registerAnnotation(RuntimeHints hints, Class<?> annotationType, boolean withProxy) {
56-
hints.reflection().registerType(annotationType, ANNOTATION_HINT);
57-
if (withProxy) {
58-
hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class);
51+
hints.reflection().registerType(annotation.getType(), ANNOTATION_HINT);
52+
MergedAnnotation<?> parentSource = annotation.getMetaSource();
53+
while (parentSource != null) {
54+
hints.reflection().registerType(parentSource.getType(), ANNOTATION_HINT);
55+
parentSource = parentSource.getMetaSource();
56+
}
57+
if (annotation.synthesize() instanceof SynthesizedAnnotation) {
58+
hints.proxies().registerJdkProxy(annotation.getType(), SynthesizedAnnotation.class);
5959
}
6060
}
6161

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

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121
import java.lang.annotation.Retention;
2222
import java.lang.annotation.RetentionPolicy;
2323
import java.lang.annotation.Target;
24+
import java.util.function.Consumer;
2425

2526
import org.junit.jupiter.api.Test;
2627

28+
import org.springframework.aot.hint.JdkProxyHint;
2729
import org.springframework.aot.hint.MemberCategory;
30+
import org.springframework.aot.hint.ReflectionHints;
2831
import org.springframework.aot.hint.RuntimeHints;
32+
import org.springframework.aot.hint.TypeHint;
2933
import org.springframework.aot.hint.TypeReference;
3034
import org.springframework.core.annotation.AliasFor;
3135
import org.springframework.core.annotation.MergedAnnotations;
@@ -46,28 +50,49 @@ class RuntimeHintsUtilsTests {
4650
void registerAnnotation() {
4751
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
4852
.from(SampleInvokerClass.class).get(SampleInvoker.class));
49-
assertThat(this.hints.reflection().getTypeHint(SampleInvoker.class)).satisfies(typeHint -> {
50-
assertThat(typeHint.constructors()).isEmpty();
51-
assertThat(typeHint.fields()).isEmpty();
52-
assertThat(typeHint.methods()).isEmpty();
53-
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
54-
});
53+
assertThat(this.hints.reflection().typeHints()).singleElement()
54+
.satisfies(annotationHint(SampleInvoker.class));
5555
assertThat(this.hints.proxies().jdkProxies()).isEmpty();
5656
}
5757

5858
@Test
5959
void registerAnnotationProxyRegistersJdkProxy() {
6060
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
6161
.from(RetryInvokerClass.class).get(RetryInvoker.class));
62-
assertThat(this.hints.reflection().getTypeHint(RetryInvoker.class)).satisfies(typeHint -> {
62+
assertThat(this.hints.reflection().typeHints()).singleElement()
63+
.satisfies(annotationHint(RetryInvoker.class));
64+
assertThat(this.hints.proxies().jdkProxies()).singleElement()
65+
.satisfies(annotationProxy(RetryInvoker.class));
66+
}
67+
68+
@Test
69+
void registerAnnotationWhereUsedAsAMetaAnnotationRegistersHierarchy() {
70+
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
71+
.from(RetryWithEnabledFlagInvokerClass.class).get(SampleInvoker.class));
72+
ReflectionHints reflection = this.hints.reflection();
73+
assertThat(reflection.typeHints())
74+
.anySatisfy(annotationHint(SampleInvoker.class))
75+
.anySatisfy(annotationHint(RetryInvoker.class))
76+
.anySatisfy(annotationHint(RetryWithEnabledFlagInvoker.class))
77+
.hasSize(3);
78+
assertThat(this.hints.proxies().jdkProxies()).singleElement()
79+
.satisfies(annotationProxy(SampleInvoker.class));
80+
}
81+
82+
private Consumer<TypeHint> annotationHint(Class<?> type) {
83+
return typeHint -> {
84+
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(type));
6385
assertThat(typeHint.constructors()).isEmpty();
6486
assertThat(typeHint.fields()).isEmpty();
6587
assertThat(typeHint.methods()).isEmpty();
6688
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
67-
});
68-
assertThat(this.hints.proxies().jdkProxies()).anySatisfy(jdkProxyHint ->
69-
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(
70-
TypeReference.of(RetryInvoker.class), TypeReference.of(SynthesizedAnnotation.class)));
89+
};
90+
}
91+
92+
private Consumer<JdkProxyHint> annotationProxy(Class<?> type) {
93+
return jdkProxyHint -> assertThat(jdkProxyHint.getProxiedInterfaces())
94+
.containsExactly(TypeReference.of(type),
95+
TypeReference.of(SynthesizedAnnotation.class));
7196
}
7297

7398

@@ -81,6 +106,11 @@ static class RetryInvokerClass {
81106

82107
}
83108

109+
@RetryWithEnabledFlagInvoker
110+
static class RetryWithEnabledFlagInvokerClass {
111+
112+
}
113+
84114

85115
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
86116
@Retention(RetentionPolicy.RUNTIME)
@@ -102,4 +132,17 @@ static class RetryInvokerClass {
102132

103133
}
104134

135+
@Target({ ElementType.TYPE })
136+
@Retention(RetentionPolicy.RUNTIME)
137+
@Documented
138+
@RetryInvoker
139+
@interface RetryWithEnabledFlagInvoker {
140+
141+
@AliasFor(attribute = "value", annotation = RetryInvoker.class)
142+
int value() default 5;
143+
144+
boolean enabled() default true;
145+
146+
}
147+
105148
}

0 commit comments

Comments
 (0)