Skip to content

Commit 6d688e1

Browse files
committed
Restore support of list of inner bean definitions
This commit restores the support of multiple bean definitions being specified in a `List` as a property value or constructor argument. Rather than handling inner bean definitions externally, there are now supported by BeanDefinitionPropertiesCodeGenerator, and list of such type is handled transparently. Closes gh-29075
1 parent 6bdf0bc commit 6d688e1

8 files changed

+138
-62
lines changed

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGenerator.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class BeanDefinitionMethodGenerator {
5757
private final Executable constructorOrFactoryMethod;
5858

5959
@Nullable
60-
private final String innerBeanPropertyName;
60+
private final String currentPropertyName;
6161

6262
private final List<BeanRegistrationAotContribution> aotContributions;
6363

@@ -66,18 +66,18 @@ class BeanDefinitionMethodGenerator {
6666
* Create a new {@link BeanDefinitionMethodGenerator} instance.
6767
* @param methodGeneratorFactory the method generator factory
6868
* @param registeredBean the registered bean
69-
* @param innerBeanPropertyName the inner bean property name
69+
* @param currentPropertyName the current property name
7070
* @param aotContributions the AOT contributions
7171
*/
7272
BeanDefinitionMethodGenerator(
7373
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory,
74-
RegisteredBean registeredBean, @Nullable String innerBeanPropertyName,
74+
RegisteredBean registeredBean, @Nullable String currentPropertyName,
7575
List<BeanRegistrationAotContribution> aotContributions) {
7676

7777
this.methodGeneratorFactory = methodGeneratorFactory;
7878
this.registeredBean = registeredBean;
7979
this.constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
80-
this.innerBeanPropertyName = innerBeanPropertyName;
80+
this.currentPropertyName = currentPropertyName;
8181
this.aotContributions = aotContributions;
8282
}
8383

@@ -188,8 +188,8 @@ private GeneratedMethod generateBeanDefinitionMethod(
188188
}
189189

190190
private String getName() {
191-
if (this.innerBeanPropertyName != null) {
192-
return this.innerBeanPropertyName;
191+
if (this.currentPropertyName != null) {
192+
return this.currentPropertyName;
193193
}
194194
if (!this.registeredBean.isGeneratedBeanName()) {
195195
return getSimpleBeanName(this.registeredBean.getBeanName());

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactory.java

+23-6
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@
3535
* {@link RegisteredBean}.
3636
*
3737
* @author Phillip Webb
38+
* @author Stephane Nicoll
3839
* @since 6.0
3940
* @see BeanDefinitionMethodGenerator
40-
* @see #getBeanDefinitionMethodGenerator(RegisteredBean, String)
41+
* @see #getBeanDefinitionMethodGenerator(RegisteredBean)
4142
*/
4243
class BeanDefinitionMethodGeneratorFactory {
4344

@@ -79,26 +80,42 @@ class BeanDefinitionMethodGeneratorFactory {
7980

8081
/**
8182
* Return a {@link BeanDefinitionMethodGenerator} for the given
82-
* {@link RegisteredBean} or {@code null} if the registered bean is excluded
83-
* by a {@link BeanRegistrationExcludeFilter}. The resulting
83+
* {@link RegisteredBean} defined with the specified property name, or
84+
* {@code null} if the registered bean is excluded by a
85+
* {@link BeanRegistrationExcludeFilter}. The resulting
8486
* {@link BeanDefinitionMethodGenerator} will include all
8587
* {@link BeanRegistrationAotProcessor} provided contributions.
8688
* @param registeredBean the registered bean
87-
* @param innerBeanPropertyName the inner bean property name or {@code null}
89+
* @param currentPropertyName the property name that this bean belongs to
8890
* @return a new {@link BeanDefinitionMethodGenerator} instance or
8991
* {@code null}
9092
*/
9193
@Nullable
9294
BeanDefinitionMethodGenerator getBeanDefinitionMethodGenerator(
93-
RegisteredBean registeredBean, @Nullable String innerBeanPropertyName) {
95+
RegisteredBean registeredBean, @Nullable String currentPropertyName) {
9496

9597
if (isExcluded(registeredBean)) {
9698
return null;
9799
}
98100
List<BeanRegistrationAotContribution> contributions = getAotContributions(
99101
registeredBean);
100102
return new BeanDefinitionMethodGenerator(this, registeredBean,
101-
innerBeanPropertyName, contributions);
103+
currentPropertyName, contributions);
104+
}
105+
106+
/**
107+
* Return a {@link BeanDefinitionMethodGenerator} for the given
108+
* {@link RegisteredBean} or {@code null} if the registered bean is excluded
109+
* by a {@link BeanRegistrationExcludeFilter}. The resulting
110+
* {@link BeanDefinitionMethodGenerator} will include all
111+
* {@link BeanRegistrationAotProcessor} provided contributions.
112+
* @param registeredBean the registered bean
113+
* @return a new {@link BeanDefinitionMethodGenerator} instance or
114+
* {@code null}
115+
*/
116+
@Nullable
117+
BeanDefinitionMethodGenerator getBeanDefinitionMethodGenerator(RegisteredBean registeredBean) {
118+
return getBeanDefinitionMethodGenerator(registeredBean, null);
102119
}
103120

104121
private boolean isExcluded(RegisteredBean registeredBean) {

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java

+36-16
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.beans.PropertyDescriptor;
2020
import java.lang.reflect.Method;
21+
import java.util.ArrayDeque;
2122
import java.util.Arrays;
2223
import java.util.Collections;
2324
import java.util.HashMap;
@@ -81,8 +82,6 @@ class BeanDefinitionPropertiesCodeGenerator {
8182

8283
private final Predicate<String> attributeFilter;
8384

84-
private final BiFunction<String, Object, CodeBlock> customValueCodeGenerator;
85-
8685
private final BeanDefinitionPropertyValueCodeGenerator valueCodeGenerator;
8786

8887

@@ -92,9 +91,8 @@ class BeanDefinitionPropertiesCodeGenerator {
9291

9392
this.hints = hints;
9493
this.attributeFilter = attributeFilter;
95-
this.customValueCodeGenerator = customValueCodeGenerator;
96-
this.valueCodeGenerator = new BeanDefinitionPropertyValueCodeGenerator(
97-
generatedMethods);
94+
this.valueCodeGenerator = new BeanDefinitionPropertyValueCodeGenerator(generatedMethods,
95+
(object, type) -> customValueCodeGenerator.apply(PropertyNamesStack.peek(), object));
9896
}
9997

10098

@@ -150,12 +148,7 @@ private void addConstructorArgumentValues(CodeBlock.Builder code,
150148
.getConstructorArgumentValues().getIndexedArgumentValues();
151149
if (!argumentValues.isEmpty()) {
152150
argumentValues.forEach((index, valueHolder) -> {
153-
String name = valueHolder.getName();
154-
Object value = valueHolder.getValue();
155-
CodeBlock valueCode = this.customValueCodeGenerator.apply(name, value);
156-
if (valueCode == null) {
157-
valueCode = this.valueCodeGenerator.generateCode(value);
158-
}
151+
CodeBlock valueCode = generateValue(valueHolder.getName(), valueHolder.getValue());
159152
code.addStatement(
160153
"$L.getConstructorArgumentValues().addIndexedArgumentValue($L, $L)",
161154
BEAN_DEFINITION_VARIABLE, index, valueCode);
@@ -170,11 +163,7 @@ private void addPropertyValues(CodeBlock.Builder code,
170163
if (!propertyValues.isEmpty()) {
171164
for (PropertyValue propertyValue : propertyValues) {
172165
String name = propertyValue.getName();
173-
Object value = propertyValue.getValue();
174-
CodeBlock valueCode = this.customValueCodeGenerator.apply(name, value);
175-
if (valueCode == null) {
176-
valueCode = this.valueCodeGenerator.generateCode(value);
177-
}
166+
CodeBlock valueCode = generateValue(name, propertyValue.getValue());
178167
code.addStatement("$L.getPropertyValues().addPropertyValue($S, $L)",
179168
BEAN_DEFINITION_VARIABLE, propertyValue.getName(), valueCode);
180169
}
@@ -191,6 +180,16 @@ private void addPropertyValues(CodeBlock.Builder code,
191180
}
192181
}
193182

183+
private CodeBlock generateValue(@Nullable String name, @Nullable Object value) {
184+
try {
185+
PropertyNamesStack.push(name);
186+
return this.valueCodeGenerator.generateCode(value);
187+
}
188+
finally {
189+
PropertyNamesStack.pop();
190+
}
191+
}
192+
194193
private Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {
195194
if (beanDefinition.hasBeanClass()) {
196195
Class<?> beanClass = beanDefinition.getBeanClass();
@@ -282,4 +281,25 @@ private <B extends BeanDefinition, T> void addStatementForValue(
282281
}
283282
}
284283

284+
static class PropertyNamesStack {
285+
286+
private static final ThreadLocal<ArrayDeque<String>> threadLocal = ThreadLocal.withInitial(ArrayDeque::new);
287+
288+
static void push(@Nullable String name) {
289+
String valueToSet = (name != null) ? name : "";
290+
threadLocal.get().push(valueToSet);
291+
}
292+
293+
static void pop() {
294+
threadLocal.get().pop();
295+
}
296+
297+
@Nullable
298+
static String peek() {
299+
String value = threadLocal.get().peek();
300+
return ("".equals(value) ? null : value);
301+
}
302+
303+
}
304+
285305
}

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertyValueCodeGenerator.java

+27-19
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.beans.factory.aot;
1818

1919
import java.nio.charset.Charset;
20+
import java.util.ArrayList;
2021
import java.util.Arrays;
2122
import java.util.Collection;
2223
import java.util.Collections;
@@ -29,6 +30,7 @@
2930
import java.util.Set;
3031
import java.util.TreeMap;
3132
import java.util.TreeSet;
33+
import java.util.function.BiFunction;
3234
import java.util.stream.Stream;
3335

3436
import org.springframework.aot.generate.GeneratedMethod;
@@ -61,26 +63,32 @@ class BeanDefinitionPropertyValueCodeGenerator {
6163

6264
private final GeneratedMethods generatedMethods;
6365

64-
private final List<Delegate> delegates = List.of(
65-
new PrimitiveDelegate(),
66-
new StringDelegate(),
67-
new CharsetDelegate(),
68-
new EnumDelegate(),
69-
new ClassDelegate(),
70-
new ResolvableTypeDelegate(),
71-
new ArrayDelegate(),
72-
new ManagedListDelegate(),
73-
new ManagedSetDelegate(),
74-
new ManagedMapDelegate(),
75-
new ListDelegate(),
76-
new SetDelegate(),
77-
new MapDelegate(),
78-
new BeanReferenceDelegate()
79-
);
80-
81-
82-
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods) {
66+
private final List<Delegate> delegates;
67+
68+
69+
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods,
70+
@Nullable BiFunction<Object, ResolvableType, CodeBlock> customValueGenerator) {
8371
this.generatedMethods = generatedMethods;
72+
this.delegates = new ArrayList<>();
73+
if (customValueGenerator != null) {
74+
this.delegates.add(customValueGenerator::apply);
75+
}
76+
this.delegates.addAll(List.of(
77+
new PrimitiveDelegate(),
78+
new StringDelegate(),
79+
new CharsetDelegate(),
80+
new EnumDelegate(),
81+
new ClassDelegate(),
82+
new ResolvableTypeDelegate(),
83+
new ArrayDelegate(),
84+
new ManagedListDelegate(),
85+
new ManagedSetDelegate(),
86+
new ManagedMapDelegate(),
87+
new ListDelegate(),
88+
new SetDelegate(),
89+
new MapDelegate(),
90+
new BeanReferenceDelegate()
91+
));
8492
}
8593

8694

Diff for: spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanRegistrationsAotProcessor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public BeanRegistrationsAotContribution processAheadOfTime(ConfigurableListableB
3939
for (String beanName : beanFactory.getBeanDefinitionNames()) {
4040
RegisteredBean registeredBean = RegisteredBean.of(beanFactory, beanName);
4141
BeanDefinitionMethodGenerator beanDefinitionMethodGenerator = beanDefinitionMethodGeneratorFactory
42-
.getBeanDefinitionMethodGenerator(registeredBean, null);
42+
.getBeanDefinitionMethodGenerator(registeredBean);
4343
if (beanDefinitionMethodGenerator != null) {
4444
registrations.put(beanName, beanDefinitionMethodGenerator);
4545
}

Diff for: spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorFactoryTests.java

+7-10
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,7 @@ void getBeanDefinitionMethodGeneratorWhenExcludedByBeanRegistrationExcludeFilter
6666
RegisteredBean registeredBean = registerTestBean(beanFactory);
6767
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
6868
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
69-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
70-
null)).isNull();
69+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean)).isNull();
7170
}
7271

7372
@Test
@@ -79,8 +78,7 @@ void getBeanDefinitionMethodGeneratorWhenExcludedByBeanRegistrationExcludeFilter
7978
new MockBeanRegistrationExcludeFilter(true, 0));
8079
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
8180
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
82-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
83-
null)).isNull();
81+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean)).isNull();
8482
}
8583

8684
@Test
@@ -100,8 +98,7 @@ void getBeanDefinitionMethodGeneratorConsidersFactoryLoadedExcludeFiltersAndBean
10098
RegisteredBean registeredBean = registerTestBean(beanFactory);
10199
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
102100
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
103-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean,
104-
null)).isNull();
101+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean)).isNull();
105102
assertThat(filter1.wasCalled()).isTrue();
106103
assertThat(filter2.wasCalled()).isTrue();
107104
assertThat(filter3.wasCalled()).isTrue();
@@ -127,7 +124,7 @@ void getBeanDefinitionMethodGeneratorAddsContributionsFromProcessors() {
127124
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
128125
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
129126
BeanDefinitionMethodGenerator methodGenerator = methodGeneratorFactory
130-
.getBeanDefinitionMethodGenerator(registeredBean, null);
127+
.getBeanDefinitionMethodGenerator(registeredBean);
131128
assertThat(methodGenerator).extracting("aotContributions").asList()
132129
.containsExactly(beanContribution, loaderContribution);
133130
}
@@ -144,8 +141,8 @@ void getBeanDefinitionMethodGeneratorWhenRegisteredBeanIsAotProcessorFiltersBean
144141
RegisteredBean registeredBean2 = RegisteredBean.of(beanFactory, "test2");
145142
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
146143
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
147-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1, null)).isNull();
148-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean2, null)).isNull();
144+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1)).isNull();
145+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean2)).isNull();
149146
}
150147

151148
@Test
@@ -157,7 +154,7 @@ void getBeanDefinitionMethodGeneratorWhenRegisteredBeanIsAotProcessorAndIsNotExc
157154
RegisteredBean registeredBean1 = RegisteredBean.of(beanFactory, "test");
158155
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory = new BeanDefinitionMethodGeneratorFactory(
159156
AotServices.factoriesAndBeans(springFactoriesLoader, beanFactory));
160-
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1, null)).isNotNull();
157+
assertThat(methodGeneratorFactory.getBeanDefinitionMethodGenerator(registeredBean1)).isNotNull();
161158
}
162159

163160
private RegisteredBean registerTestBean(DefaultListableBeanFactory beanFactory) {

Diff for: spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionMethodGeneratorTests.java

+32
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3939
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4040
import org.springframework.beans.factory.support.InstanceSupplier;
41+
import org.springframework.beans.factory.support.ManagedList;
4142
import org.springframework.beans.factory.support.RegisteredBean;
4243
import org.springframework.beans.factory.support.RootBeanDefinition;
4344
import org.springframework.beans.testfixture.beans.AnnotatedBean;
@@ -380,6 +381,37 @@ void generateBeanDefinitionMethodWhenHasInnerBeanPropertyValueGeneratesMethod()
380381
});
381382
}
382383

384+
@SuppressWarnings("unchecked")
385+
@Test
386+
void generateBeanDefinitionMethodWhenHasListOfInnerBeansPropertyValueGeneratesMethod() {
387+
RootBeanDefinition firstInnerBeanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
388+
.rootBeanDefinition(TestBean.class).addPropertyValue("name", "one")
389+
.getBeanDefinition();
390+
RootBeanDefinition secondInnerBeanDefinition = (RootBeanDefinition) BeanDefinitionBuilder
391+
.rootBeanDefinition(TestBean.class).addPropertyValue("name", "two")
392+
.getBeanDefinition();
393+
ManagedList<RootBeanDefinition> list = new ManagedList<>();
394+
list.add(firstInnerBeanDefinition);
395+
list.add(secondInnerBeanDefinition);
396+
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
397+
beanDefinition.getPropertyValues().add("someList", list);
398+
RegisteredBean registeredBean = registerBean(beanDefinition);
399+
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
400+
this.methodGeneratorFactory, registeredBean, null,
401+
Collections.emptyList());
402+
MethodReference method = generator.generateBeanDefinitionMethod(
403+
this.generationContext, this.beanRegistrationsCode);
404+
compile(method, (actual, compiled) -> {
405+
ManagedList<RootBeanDefinition> actualPropertyValue = (ManagedList<RootBeanDefinition>) actual
406+
.getPropertyValues().get("someList");
407+
assertThat(actualPropertyValue).isNotNull().hasSize(2);
408+
assertThat(actualPropertyValue.get(0).getPropertyValues().get("name")).isEqualTo("one");
409+
assertThat(actualPropertyValue.get(1).getPropertyValues().get("name")).isEqualTo("two");
410+
assertThat(compiled.getSourceFileFromPackage(TestBean.class.getPackageName()))
411+
.contains("getSomeListBeanDefinition()", "getSomeListBeanDefinition1()");
412+
});
413+
}
414+
383415
@Test
384416
void generateBeanDefinitionMethodWhenHasInnerBeanConstructorValueGeneratesMethod() {
385417
RootBeanDefinition innerBeanDefinition = (RootBeanDefinition) BeanDefinitionBuilder

0 commit comments

Comments
 (0)