Browse Source

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
pull/28515/head
Stephane Nicoll 4 years ago
parent
commit
2f94713078
  1. 16
      spring-core/src/main/java/org/springframework/aot/hint/support/RuntimeHintsUtils.java
  2. 65
      spring-core/src/test/java/org/springframework/aot/hint/support/RuntimeHintsUtilsTests.java

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

@ -48,14 +48,14 @@ public abstract class RuntimeHintsUtils { @@ -48,14 +48,14 @@ public abstract class RuntimeHintsUtils {
* @see SynthesizedAnnotation
*/
public static void registerAnnotation(RuntimeHints hints, MergedAnnotation<?> annotation) {
registerAnnotation(hints, annotation.getType(),
annotation.synthesize() instanceof SynthesizedAnnotation);
}
private static void registerAnnotation(RuntimeHints hints, Class<?> annotationType, boolean withProxy) {
hints.reflection().registerType(annotationType, ANNOTATION_HINT);
if (withProxy) {
hints.proxies().registerJdkProxy(annotationType, SynthesizedAnnotation.class);
hints.reflection().registerType(annotation.getType(), ANNOTATION_HINT);
MergedAnnotation<?> parentSource = annotation.getMetaSource();
while (parentSource != null) {
hints.reflection().registerType(parentSource.getType(), ANNOTATION_HINT);
parentSource = parentSource.getMetaSource();
}
if (annotation.synthesize() instanceof SynthesizedAnnotation) {
hints.proxies().registerJdkProxy(annotation.getType(), SynthesizedAnnotation.class);
}
}

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

@ -21,11 +21,15 @@ import java.lang.annotation.ElementType; @@ -21,11 +21,15 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.function.Consumer;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.JdkProxyHint;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
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;
@ -46,12 +50,8 @@ class RuntimeHintsUtilsTests { @@ -46,12 +50,8 @@ class RuntimeHintsUtilsTests {
void registerAnnotation() {
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
.from(SampleInvokerClass.class).get(SampleInvoker.class));
assertThat(this.hints.reflection().getTypeHint(SampleInvoker.class)).satisfies(typeHint -> {
assertThat(typeHint.constructors()).isEmpty();
assertThat(typeHint.fields()).isEmpty();
assertThat(typeHint.methods()).isEmpty();
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
});
assertThat(this.hints.reflection().typeHints()).singleElement()
.satisfies(annotationHint(SampleInvoker.class));
assertThat(this.hints.proxies().jdkProxies()).isEmpty();
}
@ -59,15 +59,40 @@ class RuntimeHintsUtilsTests { @@ -59,15 +59,40 @@ class RuntimeHintsUtilsTests {
void registerAnnotationProxyRegistersJdkProxy() {
RuntimeHintsUtils.registerAnnotation(this.hints, MergedAnnotations
.from(RetryInvokerClass.class).get(RetryInvoker.class));
assertThat(this.hints.reflection().getTypeHint(RetryInvoker.class)).satisfies(typeHint -> {
assertThat(this.hints.reflection().typeHints()).singleElement()
.satisfies(annotationHint(RetryInvoker.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));
ReflectionHints reflection = this.hints.reflection();
assertThat(reflection.typeHints())
.anySatisfy(annotationHint(SampleInvoker.class))
.anySatisfy(annotationHint(RetryInvoker.class))
.anySatisfy(annotationHint(RetryWithEnabledFlagInvoker.class))
.hasSize(3);
assertThat(this.hints.proxies().jdkProxies()).singleElement()
.satisfies(annotationProxy(SampleInvoker.class));
}
private Consumer<TypeHint> annotationHint(Class<?> type) {
return typeHint -> {
assertThat(typeHint.getType()).isEqualTo(TypeReference.of(type));
assertThat(typeHint.constructors()).isEmpty();
assertThat(typeHint.fields()).isEmpty();
assertThat(typeHint.methods()).isEmpty();
assertThat(typeHint.getMemberCategories()).containsOnly(MemberCategory.INVOKE_PUBLIC_METHODS);
});
assertThat(this.hints.proxies().jdkProxies()).anySatisfy(jdkProxyHint ->
assertThat(jdkProxyHint.getProxiedInterfaces()).containsExactly(
TypeReference.of(RetryInvoker.class), TypeReference.of(SynthesizedAnnotation.class)));
};
}
private Consumer<JdkProxyHint> annotationProxy(Class<?> type) {
return jdkProxyHint -> assertThat(jdkProxyHint.getProxiedInterfaces())
.containsExactly(TypeReference.of(type),
TypeReference.of(SynthesizedAnnotation.class));
}
@ -81,6 +106,11 @@ class RuntimeHintsUtilsTests { @@ -81,6 +106,11 @@ class RuntimeHintsUtilsTests {
}
@RetryWithEnabledFlagInvoker
static class RetryWithEnabledFlagInvokerClass {
}
@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@ -102,4 +132,17 @@ class RuntimeHintsUtilsTests { @@ -102,4 +132,17 @@ class RuntimeHintsUtilsTests {
}
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RetryInvoker
@interface RetryWithEnabledFlagInvoker {
@AliasFor(attribute = "value", annotation = RetryInvoker.class)
int value() default 5;
boolean enabled() default true;
}
}

Loading…
Cancel
Save