Apply property hints to FactoryBean if necessary
This commit handles a BeanDefinition that configures the FactoryBean as the "beanClass", while exposing the actual type in "resolvedType". While unusual, this is required in certain cases when the factory bean exposes generic information itself. Previously, the hints for properties injection were applied on the user type. Closes gh-28913
This commit is contained in:
@@ -40,6 +40,7 @@ import org.springframework.beans.BeanInfoFactory;
|
||||
import org.springframework.beans.ExtendedBeanInfoFactory;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
@@ -173,7 +174,7 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
|
||||
private void addPropertyValues(CodeBlock.Builder builder,
|
||||
BeanDefinition beanDefinition) {
|
||||
RootBeanDefinition beanDefinition) {
|
||||
|
||||
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
|
||||
if (!propertyValues.isEmpty()) {
|
||||
@@ -187,9 +188,8 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
builder.addStatement("$L.getPropertyValues().addPropertyValue($S, $L)",
|
||||
BEAN_DEFINITION_VARIABLE, propertyValue.getName(), code);
|
||||
}
|
||||
Class<?> beanType = ClassUtils
|
||||
.getUserClass(beanDefinition.getResolvableType().toClass());
|
||||
BeanInfo beanInfo = (beanType != Object.class) ? getBeanInfo(beanType) : null;
|
||||
Class<?> infrastructureType = getInfrastructureType(beanDefinition);
|
||||
BeanInfo beanInfo = (infrastructureType != Object.class) ? getBeanInfo(infrastructureType) : null;
|
||||
if (beanInfo != null) {
|
||||
Map<String, Method> writeMethods = getWriteMethods(beanInfo);
|
||||
for (PropertyValue propertyValue : propertyValues) {
|
||||
@@ -202,6 +202,16 @@ class BeanDefinitionPropertiesCodeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {
|
||||
if (beanDefinition.hasBeanClass()) {
|
||||
Class<?> beanClass = beanDefinition.getBeanClass();
|
||||
if (FactoryBean.class.isAssignableFrom(beanClass)) {
|
||||
return beanClass;
|
||||
}
|
||||
}
|
||||
return ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BeanInfo getBeanInfo(Class<?> beanType) {
|
||||
try {
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.aot.generate.GeneratedClass;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.aot.test.generator.compile.Compiled;
|
||||
import org.springframework.aot.test.generator.compile.TestCompiler;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
@@ -46,6 +47,7 @@ import org.springframework.core.testfixture.aot.generate.TestGenerationContext;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.MethodSpec;
|
||||
import org.springframework.javapoet.ParameterizedTypeName;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -353,6 +355,20 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void propertyValuesWhenValuesOnFactoryBeanClass() {
|
||||
this.beanDefinition.setTargetType(String.class);
|
||||
this.beanDefinition.setBeanClass(PropertyValuesFactoryBean.class);
|
||||
this.beanDefinition.getPropertyValues().add("prefix", "Hello");
|
||||
this.beanDefinition.getPropertyValues().add("name", "World");
|
||||
compile((actual, compiled) -> {
|
||||
assertThat(actual.getPropertyValues().get("prefix")).isEqualTo("Hello");
|
||||
assertThat(actual.getPropertyValues().get("name")).isEqualTo("World");
|
||||
});
|
||||
String[] methodNames = { "setPrefix", "setName" };
|
||||
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, methodNames);
|
||||
}
|
||||
|
||||
@Test
|
||||
void attributesWhenAllFiltered() {
|
||||
this.beanDefinition.setAttribute("a", "A");
|
||||
@@ -460,4 +476,40 @@ class BeanDefinitionPropertiesCodeGeneratorTests {
|
||||
|
||||
}
|
||||
|
||||
static class PropertyValuesFactoryBean implements FactoryBean<String> {
|
||||
|
||||
private Class<?> prefix;
|
||||
|
||||
private String name;
|
||||
|
||||
public Class<?> getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(Class<?> prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getObject() throws Exception {
|
||||
return getPrefix() + " " + getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return String.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user