Skip to content

Commit 12d756a

Browse files
committed
Refine ReflectiveProcessorBeanRegistrationAotProcessor
Add support for processing implemented interfaces and remove Reflective from runtime hints. See spring-projectsgh-28518
1 parent 2b76a12 commit 12d756a

File tree

2 files changed

+79
-13
lines changed

2 files changed

+79
-13
lines changed

spring-context/src/main/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessor.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.core.annotation.MergedAnnotation;
4141
import org.springframework.core.annotation.MergedAnnotations;
4242
import org.springframework.lang.Nullable;
43+
import org.springframework.util.ClassUtils;
4344
import org.springframework.util.ReflectionUtils;
4445

4546
/**
@@ -48,6 +49,7 @@
4849
* underlying {@link ReflectiveProcessor} implementations.
4950
*
5051
* @author Stephane Nicoll
52+
* @author Sebastien Deleuze
5153
*/
5254
class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
5355

@@ -58,37 +60,44 @@ class ReflectiveProcessorBeanRegistrationAotProcessor implements BeanRegistratio
5860
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
5961
Class<?> beanClass = registeredBean.getBeanClass();
6062
Set<Entry> entries = new LinkedHashSet<>();
61-
if (isReflective(beanClass)) {
62-
entries.add(createEntry(beanClass));
63+
processType(entries, beanClass);
64+
for (Class<?> implementedInterface : ClassUtils.getAllInterfacesForClass(beanClass)) {
65+
processType(entries, implementedInterface);
6366
}
64-
doWithReflectiveConstructors(beanClass, constructor ->
65-
entries.add(createEntry(constructor)));
66-
ReflectionUtils.doWithFields(beanClass, field ->
67-
entries.add(createEntry(field)), this::isReflective);
68-
ReflectionUtils.doWithMethods(beanClass, method ->
69-
entries.add(createEntry(method)), this::isReflective);
7067
if (!entries.isEmpty()) {
7168
return new ReflectiveProcessorBeanRegistrationAotContribution(entries);
7269
}
7370
return null;
7471
}
7572

76-
private void doWithReflectiveConstructors(Class<?> beanClass, Consumer<Constructor<?>> consumer) {
77-
for (Constructor<?> constructor : beanClass.getDeclaredConstructors()) {
73+
private void processType(Set<Entry> entries, Class<?> typeToProcess) {
74+
if (isReflective(typeToProcess)) {
75+
entries.add(createEntry(typeToProcess));
76+
}
77+
doWithReflectiveConstructors(typeToProcess, constructor ->
78+
entries.add(createEntry(constructor)));
79+
ReflectionUtils.doWithFields(typeToProcess, field ->
80+
entries.add(createEntry(field)), this::isReflective);
81+
ReflectionUtils.doWithMethods(typeToProcess, method ->
82+
entries.add(createEntry(method)), this::isReflective);
83+
}
84+
85+
private void doWithReflectiveConstructors(Class<?> typeToProcess, Consumer<Constructor<?>> consumer) {
86+
for (Constructor<?> constructor : typeToProcess.getDeclaredConstructors()) {
7887
if (isReflective(constructor)) {
7988
consumer.accept(constructor);
8089
}
8190
}
8291
}
8392

8493
private boolean isReflective(AnnotatedElement element) {
85-
return MergedAnnotations.from(element).isPresent(Reflective.class);
94+
return MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).isPresent(Reflective.class);
8695
}
8796

8897
@SuppressWarnings("unchecked")
8998
private Entry createEntry(AnnotatedElement element) {
9099
Class<? extends ReflectiveProcessor>[] processorClasses = (Class<? extends ReflectiveProcessor>[])
91-
MergedAnnotations.from(element).get(Reflective.class).getClassArray("value");
100+
MergedAnnotations.from(element, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(Reflective.class).getClassArray("value");
92101
List<ReflectiveProcessor> processors = Arrays.stream(processorClasses).distinct()
93102
.map(processorClass -> this.processors.computeIfAbsent(processorClass, BeanUtils::instantiateClass))
94103
.toList();
@@ -125,7 +134,6 @@ public ReflectiveProcessorBeanRegistrationAotContribution(Iterable<Entry> entrie
125134
@Override
126135
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
127136
RuntimeHints runtimeHints = generationContext.getRuntimeHints();
128-
RuntimeHintsUtils.registerAnnotation(runtimeHints, Reflective.class);
129137
this.entries.forEach(entry -> {
130138
AnnotatedElement element = entry.element();
131139
entry.processor().registerReflectionHints(runtimeHints.reflection(), element);

spring-context/src/test/java/org/springframework/context/aot/ReflectiveProcessorBeanRegistrationAotProcessorTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* Tests for {@link ReflectiveProcessorBeanRegistrationAotProcessor}.
4949
*
5050
* @author Stephane Nicoll
51+
* @author Sebastien Deleuze
5152
*/
5253
class ReflectiveProcessorBeanRegistrationAotProcessorTests {
5354

@@ -109,6 +110,28 @@ void shouldRegisterAnnotationAndProxyWithAliasFor() {
109110
assertThat(RuntimeHintsPredicates.proxies().forInterfaces(RetryInvoker.class, SynthesizedAnnotation.class)).accepts(runtimeHints);
110111
}
111112

113+
@Test
114+
void shouldProcessAnnotationOnInterface() {
115+
process(SampleMethodAnnotatedBeanWithInterface.class);
116+
assertThat(this.generationContext.getRuntimeHints().reflection().getTypeHint(SampleInterface.class))
117+
.satisfies(typeHint -> assertThat(typeHint.methods()).singleElement()
118+
.satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed")));
119+
assertThat(this.generationContext.getRuntimeHints().reflection().getTypeHint(SampleMethodAnnotatedBeanWithInterface.class))
120+
.satisfies(typeHint -> assertThat(typeHint.methods()).singleElement()
121+
.satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed")));
122+
}
123+
124+
@Test
125+
void shouldProcessAnnotationOnInheritedClass() {
126+
process(SampleMethodAnnotatedBeanWithInheritance.class);
127+
assertThat(this.generationContext.getRuntimeHints().reflection().getTypeHint(SampleInheritedClass.class))
128+
.satisfies(typeHint -> assertThat(typeHint.methods()).singleElement()
129+
.satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed")));
130+
assertThat(this.generationContext.getRuntimeHints().reflection().getTypeHint(SampleMethodAnnotatedBeanWithInheritance.class))
131+
.satisfies(typeHint -> assertThat(typeHint.methods()).singleElement()
132+
.satisfies(methodHint -> assertThat(methodHint.getName()).isEqualTo("managed")));
133+
}
134+
112135
@Nullable
113136
private BeanRegistrationAotContribution createContribution(Class<?> beanClass) {
114137
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
@@ -193,6 +216,28 @@ void notManaged() {
193216

194217
}
195218

219+
static class SampleMethodAnnotatedBeanWithInterface implements SampleInterface {
220+
221+
@Override
222+
public void managed() {
223+
}
224+
225+
public void notManaged() {
226+
}
227+
228+
}
229+
230+
static class SampleMethodAnnotatedBeanWithInheritance extends SampleInheritedClass {
231+
232+
@Override
233+
public void managed() {
234+
}
235+
236+
public void notManaged() {
237+
}
238+
239+
}
240+
196241
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
197242
@Retention(RetentionPolicy.RUNTIME)
198243
@Documented
@@ -214,4 +259,17 @@ void notManaged() {
214259

215260
}
216261

262+
interface SampleInterface {
263+
264+
@Reflective
265+
void managed();
266+
}
267+
268+
static class SampleInheritedClass {
269+
270+
@Reflective
271+
void managed() {
272+
}
273+
}
274+
217275
}

0 commit comments

Comments
 (0)