Register annotation based on its type
This commit improves registerAnnotation to use the annotation type rather than a `MergedAnnotation` attribute. See gh-28497
This commit is contained in:
@@ -27,10 +27,8 @@ import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint.Builder;
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.aot.hint.annotation.ReflectiveProcessor;
|
||||
import org.springframework.aot.hint.support.RuntimeHintsUtils;
|
||||
@@ -118,8 +116,6 @@ class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistratio
|
||||
|
||||
private static class ReflectiveProcessorBeanRegistrationAotContribution implements BeanRegistrationAotContribution {
|
||||
|
||||
private static final Consumer<Builder> ANNOTATION_CUSTOMIZATIONS = hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
|
||||
private final Iterable<Entry> entries;
|
||||
|
||||
public ReflectiveProcessorBeanRegistrationAotContribution(Iterable<Entry> entries) {
|
||||
@@ -129,7 +125,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistratio
|
||||
@Override
|
||||
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
|
||||
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
|
||||
runtimeHints.reflection().registerType(Reflective.class, ANNOTATION_CUSTOMIZATIONS);
|
||||
RuntimeHintsUtils.registerAnnotation(runtimeHints, Reflective.class);
|
||||
this.entries.forEach(entry -> {
|
||||
AnnotatedElement element = entry.element();
|
||||
entry.processor().registerReflectionHints(runtimeHints.reflection(), element);
|
||||
@@ -140,7 +136,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistratio
|
||||
private void registerAnnotationIfNecessary(RuntimeHints hints, AnnotatedElement element) {
|
||||
MergedAnnotation<Reflective> reflectiveAnnotation = MergedAnnotations.from(element).get(Reflective.class);
|
||||
if (reflectiveAnnotation.getDistance() > 0) {
|
||||
RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot());
|
||||
RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot().getType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
||||
process(SampleMethodMetaAnnotatedBean.class);
|
||||
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
|
||||
assertThat(runtimeHints.reflection().getTypeHint(SampleInvoker.class)).satisfies(typeHint ->
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(runtimeHints.proxies().jdkProxies()).isEmpty();
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class ReflectiveProcessorBeanRegistrationAotProcessorTests {
|
||||
process(SampleMethodMetaAnnotatedBeanWithAlias.class);
|
||||
RuntimeHints runtimeHints = this.generationContext.getRuntimeHints();
|
||||
assertThat(runtimeHints.reflection().getTypeHint(RetryInvoker.class)).satisfies(typeHint ->
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
assertThat(runtimeHints.proxies().jdkProxies()).anySatisfy(jdkProxyHint ->
|
||||
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(
|
||||
TypeReference.of(RetryInvoker.class), TypeReference.of(SynthesizedAnnotation.class)));
|
||||
|
||||
@@ -16,13 +16,18 @@
|
||||
|
||||
package org.springframework.aot.hint.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeHint.Builder;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.SynthesizedAnnotation;
|
||||
|
||||
/**
|
||||
@@ -38,24 +43,38 @@ public abstract class RuntimeHintsUtils {
|
||||
* that its attributes are visible.
|
||||
*/
|
||||
public static final Consumer<Builder> ANNOTATION_HINT = hint ->
|
||||
hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
|
||||
/**
|
||||
* Register the necessary hints so that the specified annotation is visible
|
||||
* at runtime.
|
||||
* at runtime. If an annotation attributes aliases an attribute of another
|
||||
* annotation, it is registered as well and a JDK proxy hints is defined
|
||||
* so that the synthesized annotation can be resolved.
|
||||
* @param hints the {@link RuntimeHints} instance ot use
|
||||
* @param annotation the annotation
|
||||
* @param annotationType the annotation type
|
||||
* @see SynthesizedAnnotation
|
||||
*/
|
||||
public static void registerAnnotation(RuntimeHints hints, MergedAnnotation<?> annotation) {
|
||||
hints.reflection().registerType(annotation.getType(), ANNOTATION_HINT);
|
||||
MergedAnnotation<?> parentSource = annotation.getMetaSource();
|
||||
while (parentSource != null) {
|
||||
hints.reflection().registerType(parentSource.getType(), ANNOTATION_HINT);
|
||||
parentSource = parentSource.getMetaSource();
|
||||
public static void registerAnnotation(RuntimeHints hints, Class<?> annotationType) {
|
||||
Set<Class<?>> allAnnotations = new LinkedHashSet<>();
|
||||
allAnnotations.add(annotationType);
|
||||
collectAliasedAnnotations(allAnnotations, annotationType);
|
||||
allAnnotations.forEach(annotation -> hints.reflection().registerType(annotation, ANNOTATION_HINT));
|
||||
if (allAnnotations.size() > 1) {
|
||||
hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class);
|
||||
}
|
||||
if (annotation.synthesize() instanceof SynthesizedAnnotation) {
|
||||
hints.proxies().registerJdkProxy(annotation.getType(), SynthesizedAnnotation.class);
|
||||
}
|
||||
|
||||
private static void collectAliasedAnnotations(Set<Class<?>> types, Class<?> annotationType) {
|
||||
for (Method method : annotationType.getDeclaredMethods()) {
|
||||
MergedAnnotations methodAnnotations = MergedAnnotations.from(method);
|
||||
if (methodAnnotations.isPresent(AliasFor.class)) {
|
||||
Class<?> aliasedAnnotation = methodAnnotations.get(AliasFor.class).getClass("annotation");
|
||||
boolean process = (aliasedAnnotation != Annotation.class && !types.contains(aliasedAnnotation));
|
||||
if (process) {
|
||||
types.add(aliasedAnnotation);
|
||||
collectAliasedAnnotations(types, aliasedAnnotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.support.RuntimeHintsUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} for core annotations.
|
||||
@@ -29,9 +32,9 @@ import org.springframework.aot.hint.support.RuntimeHintsUtils;
|
||||
class CoreAnnotationsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
hints.reflection().registerType(AliasFor.class, RuntimeHintsUtils.ANNOTATION_HINT)
|
||||
.registerType(Order.class, RuntimeHintsUtils.ANNOTATION_HINT);
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
Stream.of(AliasFor.class, Order.class).forEach(annotationType ->
|
||||
RuntimeHintsUtils.registerAnnotation(hints, annotationType));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.TypeHint;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.SynthesizedAnnotation;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -47,36 +46,34 @@ class RuntimeHintsUtilsTests {
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
@Test
|
||||
void registerAnnotation() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
|
||||
.from(SampleInvokerClass.class).get(SampleInvoker.class));
|
||||
void registerAnnotationType() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, SampleInvoker.class);
|
||||
assertThat(this.hints.reflection().typeHints()).singleElement()
|
||||
.satisfies(annotationHint(SampleInvoker.class));
|
||||
assertThat(this.hints.proxies().jdkProxies()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerAnnotationProxyRegistersJdkProxy() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
|
||||
.from(RetryInvokerClass.class).get(RetryInvoker.class));
|
||||
assertThat(this.hints.reflection().typeHints()).singleElement()
|
||||
.satisfies(annotationHint(RetryInvoker.class));
|
||||
void registerAnnotationTypeProxyRegistersJdkProxy() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, RetryInvoker.class);
|
||||
assertThat(this.hints.reflection().typeHints())
|
||||
.anySatisfy(annotationHint(RetryInvoker.class))
|
||||
.anySatisfy(annotationHint(SampleInvoker.class));
|
||||
assertThat(this.hints.proxies().jdkProxies()).singleElement()
|
||||
.satisfies(annotationProxy(RetryInvoker.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerAnnotationWhereUsedAsAMetaAnnotationRegistersHierarchy() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
|
||||
.from(RetryWithEnabledFlagInvokerClass.class).get(SampleInvoker.class));
|
||||
void registerAnnotationTypeWhereUsedAsAMetaAnnotationRegistersHierarchy() {
|
||||
RuntimeHintsUtils.registerAnnotation(this.hints, RetryWithEnabledFlagInvoker.class);
|
||||
ReflectionHints reflection = this.hints.reflection();
|
||||
assertThat(reflection.typeHints())
|
||||
.anySatisfy(annotationHint(SampleInvoker.class))
|
||||
.anySatisfy(annotationHint(RetryInvoker.class))
|
||||
.anySatisfy(annotationHint(RetryWithEnabledFlagInvoker.class))
|
||||
.anySatisfy(annotationHint(RetryInvoker.class))
|
||||
.anySatisfy(annotationHint(SampleInvoker.class))
|
||||
.hasSize(3);
|
||||
assertThat(this.hints.proxies().jdkProxies()).singleElement()
|
||||
.satisfies(annotationProxy(SampleInvoker.class));
|
||||
.satisfies(annotationProxy(RetryWithEnabledFlagInvoker.class));
|
||||
}
|
||||
|
||||
private Consumer<TypeHint> annotationHint(Class<?> type) {
|
||||
@@ -85,7 +82,7 @@ class RuntimeHintsUtilsTests {
|
||||
assertThat(typeHint.constructors()).isEmpty();
|
||||
assertThat(typeHint.fields()).isEmpty();
|
||||
assertThat(typeHint.methods()).isEmpty();
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
|
||||
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,14 @@ class CoreAnnotationsRuntimeHintsRegistrarTests {
|
||||
void aliasForHasHints() {
|
||||
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(AliasFor.class)))
|
||||
.satisfies(hint -> assertThat(hint.getMemberCategories())
|
||||
.containsExactly(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
}
|
||||
|
||||
@Test
|
||||
void orderAnnotationHasHints() {
|
||||
assertThat(this.hints.reflection().getTypeHint(TypeReference.of(Order.class)))
|
||||
.satisfies(hint -> assertThat(hint.getMemberCategories())
|
||||
.containsExactly(MemberCategory.INVOKE_PUBLIC_METHODS));
|
||||
.containsExactly(MemberCategory.INVOKE_DECLARED_METHODS));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user