From c9faff74917ae78b16cc76c1f56447004208fe9a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 3 Aug 2022 10:58:41 +0200 Subject: [PATCH] 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 --- ...BeanDefinitionPropertiesCodeGenerator.java | 18 +++++-- ...efinitionPropertiesCodeGeneratorTests.java | 52 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java index 6ebf25cc4e..b37d599bca 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java @@ -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 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 { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java index d8725df503..a92cf5a6f5 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java @@ -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 { + + 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; + } + + } + }