Browse Source

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
pull/28922/head
Stephane Nicoll 2 years ago
parent
commit
c9faff7491
  1. 18
      spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java
  2. 52
      spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java

18
spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java

@ -40,6 +40,7 @@ import org.springframework.beans.BeanInfoFactory; @@ -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 { @@ -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 { @@ -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 { @@ -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 {

52
spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java

@ -31,6 +31,7 @@ import org.springframework.aot.generate.GeneratedClass; @@ -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; @@ -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 { @@ -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 { @@ -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;
}
}
}

Loading…
Cancel
Save