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
This commit is contained in:
parent
c19cedede1
commit
a409e0fd2c
|
|
@ -25,6 +25,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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.RuntimeHints;
|
||||
|
|
@ -36,6 +38,7 @@ import org.springframework.util.ReflectionUtils;
|
|||
* Process {@link Reflective} annotated elements.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* since 6.0
|
||||
*/
|
||||
public class ReflectiveRuntimeHintsRegistrar {
|
||||
|
|
@ -89,9 +92,11 @@ public class ReflectiveRuntimeHintsRegistrar {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Entry createEntry(AnnotatedElement element) {
|
||||
Class<? extends ReflectiveProcessor>[] processorClasses = (Class<? extends ReflectiveProcessor>[])
|
||||
MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(Reflective.class).getClassArray("value");
|
||||
List<ReflectiveProcessor> processors = Arrays.stream(processorClasses).distinct()
|
||||
List<Class<? extends ReflectiveProcessor>> processorClasses =
|
||||
MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY)
|
||||
.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))
|
||||
.toList();
|
||||
ReflectiveProcessor processorToUse = (processors.size() == 1 ? processors.get(0)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
|
|||
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.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
|
@ -40,6 +41,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
|||
* Tests for {@link ReflectiveRuntimeHintsRegistrar}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ReflectiveRuntimeHintsRegistrarTests {
|
||||
|
||||
|
|
@ -61,6 +63,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
|
|||
.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
|
||||
void shouldProcessAnnotationOnConstructor() {
|
||||
process(SampleConstructorAnnotatedBean.class);
|
||||
|
|
@ -236,6 +246,14 @@ class ReflectiveRuntimeHintsRegistrarTests {
|
|||
|
||||
}
|
||||
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Reflective(TestTypeHintReflectiveProcessor.class)
|
||||
@interface ReflectiveWithCustomProcessor {
|
||||
|
||||
}
|
||||
|
||||
interface SampleInterface {
|
||||
|
||||
@Reflective
|
||||
|
|
@ -251,14 +269,24 @@ class ReflectiveRuntimeHintsRegistrarTests {
|
|||
|
||||
static class SampleCustomProcessor {
|
||||
|
||||
@Reflective(TestReflectiveProcessor.class)
|
||||
@Reflective(TestMethodHintReflectiveProcessor.class)
|
||||
public String managed() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestReflectiveProcessor extends SimpleReflectiveProcessor {
|
||||
@Reflective
|
||||
@ReflectiveWithCustomProcessor
|
||||
static class SampleMultipleCustomProcessors {
|
||||
|
||||
public String managed() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class TestMethodHintReflectiveProcessor extends SimpleReflectiveProcessor {
|
||||
|
||||
@Override
|
||||
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…
Reference in New Issue