From 059b66bf26c2aa8b96847938a2eb00e7645c69e1 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 25 May 2022 10:25:53 +0200 Subject: [PATCH] Register annotation based on its type This commit improves registerAnnotation to use the annotation type rather than a `MergedAnnotation` attribute. See gh-28497 --- ...ProcessorBeanRegistrationAotProcessor.java | 8 +--- ...ssorBeanRegistrationAotProcessorTests.java | 4 +- .../aot/hint/support/RuntimeHintsUtils.java | 43 +++++++++++++------ .../CoreAnnotationsRuntimeHintsRegistrar.java | 9 ++-- .../hint/support/RuntimeHintsUtilsTests.java | 29 ++++++------- ...AnnotationsRuntimeHintsRegistrarTests.java | 4 +- 6 files changed, 56 insertions(+), 41 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java b/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java index ec100046c0c..9f789fd20c6 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java +++ b/spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java @@ -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 ANNOTATION_CUSTOMIZATIONS = hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS); - private final Iterable entries; public ReflectiveProcessorBeanRegistrationAotContribution(Iterable 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 reflectiveAnnotation = MergedAnnotations.from(element).get(Reflective.class); if (reflectiveAnnotation.getDistance() > 0) { - RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot()); + RuntimeHintsUtils.registerAnnotation(hints, reflectiveAnnotation.getRoot().getType()); } } diff --git a/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java b/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java index 62bf86e38f7..60516491bb4 100644 --- a/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java @@ -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))); diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java index dfa269629a9..6d18d41dd06 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java @@ -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 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> 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> 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); + } + } } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrar.java b/spring-core/src/main/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrar.java index 4e15a6ae929..d38e4e38a05 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrar.java @@ -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)); } } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java index b352d5a3e84..df7114aae49 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java @@ -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 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); }; } diff --git a/spring-core/src/test/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrarTests.java b/spring-core/src/test/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrarTests.java index 9e87a4ddfe5..683837f6f07 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrarTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/CoreAnnotationsRuntimeHintsRegistrarTests.java @@ -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)); } }