Skip to content

Commit 059b66b

Browse files
committed
Register annotation based on its type
This commit improves registerAnnotation to use the annotation type rather than a `MergedAnnotation` attribute. See gh-28497
1 parent a60e9d6 commit 059b66b

File tree

6 files changed

+56
-41
lines changed

6 files changed

+56
-41
lines changed

Diff for: spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java

+2-6
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,8 @@
2727
import java.util.function.Consumer;
2828

2929
import org.springframework.aot.generate.GenerationContext;
30-
import org.springframework.aot.hint.MemberCategory;
3130
import org.springframework.aot.hint.ReflectionHints;
3231
import org.springframework.aot.hint.RuntimeHints;
33-
import org.springframework.aot.hint.TypeHint.Builder;
3432
import org.springframework.aot.hint.annotation.Reflective;
3533
import org.springframework.aot.hint.annotation.ReflectiveProcessor;
3634
import org.springframework.aot.hint.support.RuntimeHintsUtils;
@@ -118,8 +116,6 @@ private record Entry(AnnotatedElement element, ReflectiveProcessor processor) {}
118116

119117
private static class ReflectiveProcessorBeanRegistrationAotContribution implements BeanRegistrationAotContribution {
120118

121-
private static final Consumer<Builder> ANNOTATION_CUSTOMIZATIONS = hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
122-
123119
private final Iterable<Entry> entries;
124120

125121
public ReflectiveProcessorBeanRegistrationAotContribution(Iterable<Entry> entries) {
@@ -129,7 +125,7 @@ public ReflectiveProcessorBeanRegistrationAotContribution(Iterable<Entry> entrie
129125
@Override
130126
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
131127
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
132-
runtimeHints.reflection().registerType(Reflective.class, ANNOTATION_CUSTOMIZATIONS);
128+
RuntimeHintsUtils.registerAnnotation(runtimeHints, Reflective.class);
133129
this.entries.forEach(entry -> {
134130
AnnotatedElement element = entry.element();
135131
entry.processor().registerReflectionHints(runtimeHints.reflection(), element);
@@ -140,7 +136,7 @@ public void applyTo(GenerationContext generationContext, BeanRegistrationCode be
140136
private void registerAnnotationIfNecessary(RuntimeHints hints, AnnotatedElement element) {
141137
MergedAnnotation<Reflective> reflectiveAnnotation = MergedAnnotations.from(element).get(Reflective.class);
142138
if (reflectiveAnnotation.getDistance() > 0) {
143-
RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot());
139+
RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot().getType());
144140
}
145141
}
146142

Diff for: spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ void shouldRegisterAnnotation() {
9797
process(SampleMethodMetaAnnotatedBean.class);
9898
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
9999
assertThat(runtimeHints.reflection().getTypeHint(SampleInvoker.class)).satisfies(typeHint ->
100-
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS));
100+
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
101101
assertThat(runtimeHints.proxies().jdkProxies()).isEmpty();
102102
}
103103

@@ -106,7 +106,7 @@ void shouldRegisterAnnotationAndProxyWithAliasFor() {
106106
process(SampleMethodMetaAnnotatedBeanWithAlias.class);
107107
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
108108
assertThat(runtimeHints.reflection().getTypeHint(RetryInvoker.class)).satisfies(typeHint ->
109-
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS));
109+
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
110110
assertThat(runtimeHints.proxies().jdkProxies()).anySatisfy(jdkProxyHint ->
111111
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(
112112
TypeReference.of(RetryInvoker.class), TypeReference.of(SynthesizedAnnotation.class)));

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

+31-12
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,18 @@
1616

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

19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.Method;
21+
import java.util.LinkedHashSet;
22+
import java.util.Set;
1923
import java.util.function.Consumer;
2024

2125
import org.springframework.aot.hint.MemberCategory;
2226
import org.springframework.aot.hint.RuntimeHints;
2327
import org.springframework.aot.hint.TypeHint;
2428
import org.springframework.aot.hint.TypeHint.Builder;
25-
import org.springframework.core.annotation.MergedAnnotation;
29+
import org.springframework.core.annotation.AliasFor;
30+
import org.springframework.core.annotation.MergedAnnotations;
2631
import org.springframework.core.annotation.SynthesizedAnnotation;
2732

2833
/**
@@ -38,24 +43,38 @@ public abstract class RuntimeHintsUtils {
3843
* that its attributes are visible.
3944
*/
4045
public static final Consumer<Builder> ANNOTATION_HINT = hint ->
41-
hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
46+
hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS);
4247

4348
/**
4449
* Register the necessary hints so that the specified annotation is visible
45-
* at runtime.
50+
* at runtime. If an annotation attributes aliases an attribute of another
51+
* annotation, it is registered as well and a JDK proxy hints is defined
52+
* so that the synthesized annotation can be resolved.
4653
* @param hints the {@link RuntimeHints} instance ot use
47-
* @param annotation the annotation
54+
* @param annotationType the annotation type
4855
* @see SynthesizedAnnotation
4956
*/
50-
public static void registerAnnotation(RuntimeHints hints, MergedAnnotation<?> annotation) {
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();
57+
public static void registerAnnotation(RuntimeHints hints, Class<?> annotationType) {
58+
Set<Class<?>> allAnnotations = new LinkedHashSet<>();
59+
allAnnotations.add(annotationType);
60+
collectAliasedAnnotations(allAnnotations, annotationType);
61+
allAnnotations.forEach(annotation -> hints.reflection().registerType(annotation, ANNOTATION_HINT));
62+
if (allAnnotations.size() > 1) {
63+
hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class);
5664
}
57-
if (annotation.synthesize() instanceof SynthesizedAnnotation) {
58-
hints.proxies().registerJdkProxy(annotation.getType(), SynthesizedAnnotation.class);
65+
}
66+
67+
private static void collectAliasedAnnotations(Set<Class<?>> types, Class<?> annotationType) {
68+
for (Method method : annotationType.getDeclaredMethods()) {
69+
MergedAnnotations methodAnnotations = MergedAnnotations.from(method);
70+
if (methodAnnotations.isPresent(AliasFor.class)) {
71+
Class<?> aliasedAnnotation = methodAnnotations.get(AliasFor.class).getClass("annotation");
72+
boolean process = (aliasedAnnotation != Annotation.class && !types.contains(aliasedAnnotation));
73+
if (process) {
74+
types.add(aliasedAnnotation);
75+
collectAliasedAnnotations(types, aliasedAnnotation);
76+
}
77+
}
5978
}
6079
}
6180

Diff for: spring-core/src/main/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrar.java

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
package org.springframework.core.annotation;
1818

19+
import java.util.stream.Stream;
20+
1921
import org.springframework.aot.hint.RuntimeHints;
2022
import org.springframework.aot.hint.RuntimeHintsRegistrar;
2123
import org.springframework.aot.hint.support.RuntimeHintsUtils;
24+
import org.springframework.lang.Nullable;
2225

2326
/**
2427
* {@link RuntimeHintsRegistrar} for core annotations.
@@ -29,9 +32,9 @@
2932
class CoreAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
3033

3134
@Override
32-
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
33-
hints.reflection().registerType(AliasFor.class, RuntimeHintsUtils.ANNOTATION_HINT)
34-
.registerType(Order.class, RuntimeHintsUtils.ANNOTATION_HINT);
35+
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
36+
Stream.of(AliasFor.class, Order.class).forEach(annotationType ->
37+
RuntimeHintsUtils.registerAnnotation(hints, annotationType));
3538
}
3639

3740
}

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

+13-16
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.aot.hint.TypeHint;
3333
import org.springframework.aot.hint.TypeReference;
3434
import org.springframework.core.annotation.AliasFor;
35-
import org.springframework.core.annotation.MergedAnnotations;
3635
import org.springframework.core.annotation.SynthesizedAnnotation;
3736

3837
import static org.assertj.core.api.Assertions.assertThat;
@@ -47,36 +46,34 @@ class RuntimeHintsUtilsTests {
4746
private final RuntimeHints hints = new RuntimeHints();
4847

4948
@Test
50-
void registerAnnotation() {
51-
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
52-
.from(SampleInvokerClass.class).get(SampleInvoker.class));
49+
void registerAnnotationType() {
50+
RuntimeHintsUtils.registerAnnotation(this.hints, SampleInvoker.class);
5351
assertThat(this.hints.reflection().typeHints()).singleElement()
5452
.satisfies(annotationHint(SampleInvoker.class));
5553
assertThat(this.hints.proxies().jdkProxies()).isEmpty();
5654
}
5755

5856
@Test
59-
void registerAnnotationProxyRegistersJdkProxy() {
60-
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
61-
.from(RetryInvokerClass.class).get(RetryInvoker.class));
62-
assertThat(this.hints.reflection().typeHints()).singleElement()
63-
.satisfies(annotationHint(RetryInvoker.class));
57+
void registerAnnotationTypeProxyRegistersJdkProxy() {
58+
RuntimeHintsUtils.registerAnnotation(this.hints, RetryInvoker.class);
59+
assertThat(this.hints.reflection().typeHints())
60+
.anySatisfy(annotationHint(RetryInvoker.class))
61+
.anySatisfy(annotationHint(SampleInvoker.class));
6462
assertThat(this.hints.proxies().jdkProxies()).singleElement()
6563
.satisfies(annotationProxy(RetryInvoker.class));
6664
}
6765

6866
@Test
69-
void registerAnnotationWhereUsedAsAMetaAnnotationRegistersHierarchy() {
70-
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
71-
.from(RetryWithEnabledFlagInvokerClass.class).get(SampleInvoker.class));
67+
void registerAnnotationTypeWhereUsedAsAMetaAnnotationRegistersHierarchy() {
68+
RuntimeHintsUtils.registerAnnotation(this.hints, RetryWithEnabledFlagInvoker.class);
7269
ReflectionHints reflection = this.hints.reflection();
7370
assertThat(reflection.typeHints())
74-
.anySatisfy(annotationHint(SampleInvoker.class))
75-
.anySatisfy(annotationHint(RetryInvoker.class))
7671
.anySatisfy(annotationHint(RetryWithEnabledFlagInvoker.class))
72+
.anySatisfy(annotationHint(RetryInvoker.class))
73+
.anySatisfy(annotationHint(SampleInvoker.class))
7774
.hasSize(3);
7875
assertThat(this.hints.proxies().jdkProxies()).singleElement()
79-
.satisfies(annotationProxy(SampleInvoker.class));
76+
.satisfies(annotationProxy(RetryWithEnabledFlagInvoker.class));
8077
}
8178

8279
private Consumer<TypeHint> annotationHint(Class<?> type) {
@@ -85,7 +82,7 @@ private Consumer<TypeHint> annotationHint(Class<?> type) {
8582
assertThat(typeHint.constructors()).isEmpty();
8683
assertThat(typeHint.fields()).isEmpty();
8784
assertThat(typeHint.methods()).isEmpty();
88-
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
85+
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS);
8986
};
9087
}
9188

Diff for: spring-core/src/test/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrarTests.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ void setup() {
4949
void aliasForHasHints() {
5050
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(AliasFor.class)))
5151
.satisfies(hint -> assertThat(hint.getMemberCategories())
52-
.containsExactly(MemberCategory.INVOKE_PUBLIC_METHODS));
52+
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
5353
}
5454

5555
@Test
5656
void orderAnnotationHasHints() {
5757
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(Order.class)))
5858
.satisfies(hint -> assertThat(hint.getMemberCategories())
59-
.containsExactly(MemberCategory.INVOKE_PUBLIC_METHODS));
59+
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
6060
}
6161

6262
}

0 commit comments

Comments
 (0)