Skip to content

Commit c9faff7

Browse files
committed
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
1 parent e79a8f6 commit c9faff7

File tree

2 files changed

+66
-4
lines changed

2 files changed

+66
-4
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.beans.ExtendedBeanInfoFactory;
4141
import org.springframework.beans.MutablePropertyValues;
4242
import org.springframework.beans.PropertyValue;
43+
import org.springframework.beans.factory.FactoryBean;
4344
import org.springframework.beans.factory.config.BeanDefinition;
4445
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
4546
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
@@ -173,7 +174,7 @@ private void addConstructorArgumentValues(CodeBlock.Builder builder,
173174
}
174175

175176
private void addPropertyValues(CodeBlock.Builder builder,
176-
BeanDefinition beanDefinition) {
177+
RootBeanDefinition beanDefinition) {
177178

178179
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
179180
if (!propertyValues.isEmpty()) {
@@ -187,9 +188,8 @@ private void addPropertyValues(CodeBlock.Builder builder,
187188
builder.addStatement("$L.getPropertyValues().addPropertyValue($S, $L)",
188189
BEAN_DEFINITION_VARIABLE, propertyValue.getName(), code);
189190
}
190-
Class<?> beanType = ClassUtils
191-
.getUserClass(beanDefinition.getResolvableType().toClass());
192-
BeanInfo beanInfo = (beanType != Object.class) ? getBeanInfo(beanType) : null;
191+
Class<?> infrastructureType = getInfrastructureType(beanDefinition);
192+
BeanInfo beanInfo = (infrastructureType != Object.class) ? getBeanInfo(infrastructureType) : null;
193193
if (beanInfo != null) {
194194
Map<String, Method> writeMethods = getWriteMethods(beanInfo);
195195
for (PropertyValue propertyValue : propertyValues) {
@@ -202,6 +202,16 @@ private void addPropertyValues(CodeBlock.Builder builder,
202202
}
203203
}
204204

205+
private Class<?> getInfrastructureType(RootBeanDefinition beanDefinition) {
206+
if (beanDefinition.hasBeanClass()) {
207+
Class<?> beanClass = beanDefinition.getBeanClass();
208+
if (FactoryBean.class.isAssignableFrom(beanClass)) {
209+
return beanClass;
210+
}
211+
}
212+
return ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
213+
}
214+
205215
@Nullable
206216
private BeanInfo getBeanInfo(Class<?> beanType) {
207217
try {

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
3232
import org.springframework.aot.test.generator.compile.Compiled;
3333
import org.springframework.aot.test.generator.compile.TestCompiler;
34+
import org.springframework.beans.factory.FactoryBean;
3435
import org.springframework.beans.factory.config.BeanDefinition;
3536
import org.springframework.beans.factory.config.BeanReference;
3637
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
@@ -46,6 +47,7 @@
4647
import org.springframework.javapoet.CodeBlock;
4748
import org.springframework.javapoet.MethodSpec;
4849
import org.springframework.javapoet.ParameterizedTypeName;
50+
import org.springframework.lang.Nullable;
4951

5052
import static org.assertj.core.api.Assertions.assertThat;
5153

@@ -353,6 +355,20 @@ void propertyValuesWhenContainsManagedMap() {
353355
});
354356
}
355357

358+
@Test
359+
void propertyValuesWhenValuesOnFactoryBeanClass() {
360+
this.beanDefinition.setTargetType(String.class);
361+
this.beanDefinition.setBeanClass(PropertyValuesFactoryBean.class);
362+
this.beanDefinition.getPropertyValues().add("prefix", "Hello");
363+
this.beanDefinition.getPropertyValues().add("name", "World");
364+
compile((actual, compiled) -> {
365+
assertThat(actual.getPropertyValues().get("prefix")).isEqualTo("Hello");
366+
assertThat(actual.getPropertyValues().get("name")).isEqualTo("World");
367+
});
368+
String[] methodNames = { "setPrefix", "setName" };
369+
assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, methodNames);
370+
}
371+
356372
@Test
357373
void attributesWhenAllFiltered() {
358374
this.beanDefinition.setAttribute("a", "A");
@@ -460,4 +476,40 @@ public void setSpring(String spring) {
460476

461477
}
462478

479+
static class PropertyValuesFactoryBean implements FactoryBean<String> {
480+
481+
private Class<?> prefix;
482+
483+
private String name;
484+
485+
public Class<?> getPrefix() {
486+
return this.prefix;
487+
}
488+
489+
public void setPrefix(Class<?> prefix) {
490+
this.prefix = prefix;
491+
}
492+
493+
public String getName() {
494+
return this.name;
495+
}
496+
497+
public void setName(String name) {
498+
this.name = name;
499+
}
500+
501+
@Nullable
502+
@Override
503+
public String getObject() throws Exception {
504+
return getPrefix() + " " + getName();
505+
}
506+
507+
@Nullable
508+
@Override
509+
public Class<?> getObjectType() {
510+
return String.class;
511+
}
512+
513+
}
514+
463515
}

0 commit comments

Comments
 (0)