Browse Source

Consider all processors when processing `@Reflective`

Previously, only the first occurance of `@Reflective` and its
processor was considered. When `@Reflective` appeared twice on a type
due to meta-annotations or inheritance, this resulted in other
processors being ignored and hints were missing as a result.

This commit updates ReflectiveRuntimeHintsRegistrar to consider
every occurance of `@Reflective` found in the type hierarchy,
and to then use the processors from each of them.

Fixes gh-29193
pull/29198/head
Andy Wilkinson 2 years ago committed by Sébastien Deleuze
parent
commit
a409e0fd2c
  1. 11
      spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java
  2. 42
      spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java

11
spring-core/src/main/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrar.java

@ -25,6 +25,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
@ -36,6 +38,7 @@ import org.springframework.util.ReflectionUtils;
* Process {@link Reflective} annotated elements. * Process {@link Reflective} annotated elements.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
* since 6.0 * since 6.0
*/ */
public class ReflectiveRuntimeHintsRegistrar { public class ReflectiveRuntimeHintsRegistrar {
@ -89,9 +92,11 @@ public class ReflectiveRuntimeHintsRegistrar {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Entry createEntry(AnnotatedElement element) { private Entry createEntry(AnnotatedElement element) {
Class<? extends ReflectiveProcessor>[] processorClasses = (Class<? extends ReflectiveProcessor>[]) List<Class<? extends ReflectiveProcessor>> processorClasses =
MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(Reflective.class).getClassArray("value"); MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
List<ReflectiveProcessor> processors = Arrays.stream(processorClasses).distinct() .stream(Reflective.class).flatMap(annotation -> Stream.of(annotation.getClassArray("value")))
.map(type -> (Class<? extends ReflectiveProcessor>) type).collect(Collectors.toList());
List<ReflectiveProcessor> processors = processorClasses.stream().distinct()
.map(processorClass -> this.processors.computeIfAbsent(processorClass, this::instantiateClass)) .map(processorClass -> this.processors.computeIfAbsent(processorClass, this::instantiateClass))
.toList(); .toList();
ReflectiveProcessor processorToUse = (processors.size() == 1 ? processors.get(0) ReflectiveProcessor processorToUse = (processors.size() == 1 ? processors.get(0)

42
spring-core/src/test/java/org/springframework/aot/hint/annotation/ReflectiveRuntimeHintsRegistrarTests.java

@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints; import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeHint;
import org.springframework.aot.hint.TypeReference; import org.springframework.aot.hint.TypeReference;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.AliasFor;
@ -40,6 +41,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
* Tests for {@link ReflectiveRuntimeHintsRegistrar}. * Tests for {@link ReflectiveRuntimeHintsRegistrar}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson
*/ */
class ReflectiveRuntimeHintsRegistrarTests { class ReflectiveRuntimeHintsRegistrarTests {
@ -61,6 +63,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
.isNotNull(); .isNotNull();
} }
@Test
void shouldProcessWithMultipleProcessorsWithAnnotationOnType() {
process(SampleMultipleCustomProcessors.class);
TypeHint typeHint = this.runtimeHints.reflection().getTypeHint(SampleMultipleCustomProcessors.class);
assertThat(typeHint).isNotNull();
assertThat(typeHint.getMemberCategories()).containsExactly(MemberCategory.INVOKE_DECLARED_METHODS);
}
@Test @Test
void shouldProcessAnnotationOnConstructor() { void shouldProcessAnnotationOnConstructor() {
process(SampleConstructorAnnotatedBean.class); process(SampleConstructorAnnotatedBean.class);
@ -236,6 +246,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
} }
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Reflective(TestTypeHintReflectiveProcessor.class)
@interface ReflectiveWithCustomProcessor {
}
interface SampleInterface { interface SampleInterface {
@Reflective @Reflective
@ -251,14 +269,24 @@ class ReflectiveRuntimeHintsRegistrarTests {
static class SampleCustomProcessor { static class SampleCustomProcessor {
@Reflective(TestReflectiveProcessor.class) @Reflective(TestMethodHintReflectiveProcessor.class)
public String managed() {
return "test";
}
}
@Reflective
@ReflectiveWithCustomProcessor
static class SampleMultipleCustomProcessors {
public String managed() { public String managed() {
return "test"; return "test";
} }
} }
private static class TestReflectiveProcessor extends SimpleReflectiveProcessor { private static class TestMethodHintReflectiveProcessor extends SimpleReflectiveProcessor {
@Override @Override
protected void registerMethodHint(ReflectionHints hints, Method method) { protected void registerMethodHint(ReflectionHints hints, Method method) {
@ -268,4 +296,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
} }
private static class TestTypeHintReflectiveProcessor extends SimpleReflectiveProcessor {
@Override
protected void registerTypeHint(ReflectionHints hints, Class<?> type) {
super.registerTypeHint(hints, type);
hints.registerType(type, MemberCategory.INVOKE_DECLARED_METHODS);
}
}
} }

Loading…
Cancel
Save