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
This commit is contained in:
Stephane Nicoll
2022-10-18 12:50:54 +02:00
parent 6bdf0bcc4a
commit 6d688e196d
8 changed files with 136 additions and 60 deletions

View File

@@ -57,7 +57,7 @@ class BeanDefinitionMethodGenerator {
private final Executable constructorOrFactoryMethod;
@Nullable
private final String innerBeanPropertyName;
private final String currentPropertyName;
private final List<BeanRegistrationAotContribution> aotContributions;
@@ -66,18 +66,18 @@ class BeanDefinitionMethodGenerator {
* Create a new {@link BeanDefinitionMethodGenerator} instance.
* @param methodGeneratorFactory the method generator factory
* @param registeredBean the registered bean
* @param innerBeanPropertyName the inner bean property name
* @param currentPropertyName the current property name
* @param aotContributions the AOT contributions
*/
BeanDefinitionMethodGenerator(
BeanDefinitionMethodGeneratorFactory methodGeneratorFactory,
RegisteredBean registeredBean, @Nullable String innerBeanPropertyName,
RegisteredBean registeredBean, @Nullable String currentPropertyName,
List<BeanRegistrationAotContribution> aotContributions) {
this.methodGeneratorFactory = methodGeneratorFactory;
this.registeredBean = registeredBean;
this.constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
this.innerBeanPropertyName = innerBeanPropertyName;
this.currentPropertyName = currentPropertyName;
this.aotContributions = aotContributions;
}
@@ -188,8 +188,8 @@ class BeanDefinitionMethodGenerator {
}
private String getName() {
if (this.innerBeanPropertyName != null) {
return this.innerBeanPropertyName;
if (this.currentPropertyName != null) {
return this.currentPropertyName;
}
if (!this.registeredBean.isGeneratedBeanName()) {
return getSimpleBeanName(this.registeredBean.getBeanName());

View File

@@ -35,9 +35,10 @@ import org.springframework.util.ObjectUtils;
* {@link RegisteredBean}.
*
* @author Phillip Webb
* @author Stephane Nicoll
* @since 6.0
* @see BeanDefinitionMethodGenerator
* @see #getBeanDefinitionMethodGenerator(RegisteredBean, String)
* @see #getBeanDefinitionMethodGenerator(RegisteredBean)
*/
class BeanDefinitionMethodGeneratorFactory {
@@ -79,18 +80,19 @@ class BeanDefinitionMethodGeneratorFactory {
/**
* Return a {@link BeanDefinitionMethodGenerator} for the given
* {@link RegisteredBean} or {@code null} if the registered bean is excluded
* by a {@link BeanRegistrationExcludeFilter}. The resulting
* {@link RegisteredBean} defined with the specified property name, or
* {@code null} if the registered bean is excluded by a
* {@link BeanRegistrationExcludeFilter}. The resulting
* {@link BeanDefinitionMethodGenerator} will include all
* {@link BeanRegistrationAotProcessor} provided contributions.
* @param registeredBean the registered bean
* @param innerBeanPropertyName the inner bean property name or {@code null}
* @param currentPropertyName the property name that this bean belongs to
* @return a new {@link BeanDefinitionMethodGenerator} instance or
* {@code null}
*/
@Nullable
BeanDefinitionMethodGenerator getBeanDefinitionMethodGenerator(
RegisteredBean registeredBean, @Nullable String innerBeanPropertyName) {
RegisteredBean registeredBean, @Nullable String currentPropertyName) {
if (isExcluded(registeredBean)) {
return null;
@@ -98,7 +100,22 @@ class BeanDefinitionMethodGeneratorFactory {
List<BeanRegistrationAotContribution> contributions = getAotContributions(
registeredBean);
return new BeanDefinitionMethodGenerator(this, registeredBean,
innerBeanPropertyName, contributions);
currentPropertyName, contributions);
}
/**
* Return a {@link BeanDefinitionMethodGenerator} for the given
* {@link RegisteredBean} or {@code null} if the registered bean is excluded
* by a {@link BeanRegistrationExcludeFilter}. The resulting
* {@link BeanDefinitionMethodGenerator} will include all
* {@link BeanRegistrationAotProcessor} provided contributions.
* @param registeredBean the registered bean
* @return a new {@link BeanDefinitionMethodGenerator} instance or
* {@code null}
*/
@Nullable
BeanDefinitionMethodGenerator getBeanDefinitionMethodGenerator(RegisteredBean registeredBean) {
return getBeanDefinitionMethodGenerator(registeredBean, null);
}
private boolean isExcluded(RegisteredBean registeredBean) {

View File

@@ -18,6 +18,7 @@ package org.springframework.beans.factory.aot;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -81,8 +82,6 @@ class BeanDefinitionPropertiesCodeGenerator {
private final Predicate<String> attributeFilter;
private final BiFunction<String, Object, CodeBlock> customValueCodeGenerator;
private final BeanDefinitionPropertyValueCodeGenerator valueCodeGenerator;
@@ -92,9 +91,8 @@ class BeanDefinitionPropertiesCodeGenerator {
this.hints = hints;
this.attributeFilter = attributeFilter;
this.customValueCodeGenerator = customValueCodeGenerator;
this.valueCodeGenerator = new BeanDefinitionPropertyValueCodeGenerator(
generatedMethods);
this.valueCodeGenerator = new BeanDefinitionPropertyValueCodeGenerator(generatedMethods,
(object, type) -> customValueCodeGenerator.apply(PropertyNamesStack.peek(), object));
}
@@ -150,12 +148,7 @@ class BeanDefinitionPropertiesCodeGenerator {
.getConstructorArgumentValues().getIndexedArgumentValues();
if (!argumentValues.isEmpty()) {
argumentValues.forEach((index, valueHolder) -> {
String name = valueHolder.getName();
Object value = valueHolder.getValue();
CodeBlock valueCode = this.customValueCodeGenerator.apply(name, value);
if (valueCode == null) {
valueCode = this.valueCodeGenerator.generateCode(value);
}
CodeBlock valueCode = generateValue(valueHolder.getName(), valueHolder.getValue());
code.addStatement(
"$L.getConstructorArgumentValues().addIndexedArgumentValue($L, $L)",
BEAN_DEFINITION_VARIABLE, index, valueCode);
@@ -170,11 +163,7 @@ class BeanDefinitionPropertiesCodeGenerator {
if (!propertyValues.isEmpty()) {
for (PropertyValue propertyValue : propertyValues) {
String name = propertyValue.getName();
Object value = propertyValue.getValue();
CodeBlock valueCode = this.customValueCodeGenerator.apply(name, value);
if (valueCode == null) {
valueCode = this.valueCodeGenerator.generateCode(value);
}
CodeBlock valueCode = generateValue(name, propertyValue.getValue());
code.addStatement("$L.getPropertyValues().addPropertyValue($S, $L)",
BEAN_DEFINITION_VARIABLE, propertyValue.getName(), valueCode);
}
@@ -191,6 +180,16 @@ class BeanDefinitionPropertiesCodeGenerator {
}
}
private CodeBlock generateValue(@Nullable String name, @Nullable Object value) {
try {
PropertyNamesStack.push(name);
return this.valueCodeGenerator.generateCode(value);
}
finally {
PropertyNamesStack.pop();
}
}
private Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {
if (beanDefinition.hasBeanClass()) {
Class<?> beanClass = beanDefinition.getBeanClass();
@@ -282,4 +281,25 @@ class BeanDefinitionPropertiesCodeGenerator {
}
}
static class PropertyNamesStack {
private static final ThreadLocal<ArrayDeque<String>> threadLocal = ThreadLocal.withInitial(ArrayDeque::new);
static void push(@Nullable String name) {
String valueToSet = (name != null) ? name : "";
threadLocal.get().push(valueToSet);
}
static void pop() {
threadLocal.get().pop();
}
@Nullable
static String peek() {
String value = threadLocal.get().peek();
return ("".equals(value) ? null : value);
}
}
}

View File

@@ -17,6 +17,7 @@
package org.springframework.beans.factory.aot;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -29,6 +30,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.stream.Stream;
import org.springframework.aot.generate.GeneratedMethod;
@@ -61,26 +63,32 @@ class BeanDefinitionPropertyValueCodeGenerator {
private final GeneratedMethods generatedMethods;
private final List<Delegate> delegates = List.of(
new PrimitiveDelegate(),
new StringDelegate(),
new CharsetDelegate(),
new EnumDelegate(),
new ClassDelegate(),
new ResolvableTypeDelegate(),
new ArrayDelegate(),
new ManagedListDelegate(),
new ManagedSetDelegate(),
new ManagedMapDelegate(),
new ListDelegate(),
new SetDelegate(),
new MapDelegate(),
new BeanReferenceDelegate()
);
private final List<Delegate> delegates;
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods) {
BeanDefinitionPropertyValueCodeGenerator(GeneratedMethods generatedMethods,
@Nullable BiFunction<Object, ResolvableType, CodeBlock> customValueGenerator) {
this.generatedMethods = generatedMethods;
this.delegates = new ArrayList<>();
if (customValueGenerator != null) {
this.delegates.add(customValueGenerator::apply);
}
this.delegates.addAll(List.of(
new PrimitiveDelegate(),
new StringDelegate(),
new CharsetDelegate(),
new EnumDelegate(),
new ClassDelegate(),
new ResolvableTypeDelegate(),
new ArrayDelegate(),
new ManagedListDelegate(),
new ManagedSetDelegate(),
new ManagedMapDelegate(),
new ListDelegate(),
new SetDelegate(),
new MapDelegate(),
new BeanReferenceDelegate()
));
}

View File

@@ -39,7 +39,7 @@ class BeanRegistrationsAotProcessor implements BeanFactoryInitializationAotProce
for (String beanName : beanFactory.getBeanDefinitionNames()) {
RegisteredBean registeredBean = RegisteredBean.of(beanFactory, beanName);
BeanDefinitionMethodGenerator beanDefinitionMethodGenerator = beanDefinitionMethodGeneratorFactory
.getBeanDefinitionMethodGenerator(registeredBean, null);
.getBeanDefinitionMethodGenerator(registeredBean);
if (beanDefinitionMethodGenerator != null) {
registrations.put(beanName, beanDefinitionMethodGenerator);
}