Consistently set target type in generated code

Previously, a bean definition that is optimized AOT could have
different metadata based on whether its resolved type had a generic or
not. This is due to RootBeanDefinition taking either a Class or a
ResolvableType doing fundamentally different things. While the former
sets the bean class which is to little use with an instance supplier,
the latter specifies the target type of the bean.

This commit sets the target type of the bean, using the existing
setter methods that take either a class or a ResolvableType and set the
same attribute consistently.

Closes gh-30689
This commit is contained in:
Stephane Nicoll
2023-06-20 16:44:01 +02:00
parent 4879b56bb9
commit 6c42f374c8
4 changed files with 180 additions and 18 deletions

View File

@@ -47,6 +47,10 @@ import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.beans.testfixture.beans.factory.aot.InnerBeanConfiguration;
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Implementation;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.One;
import org.springframework.beans.testfixture.beans.factory.aot.TestHierarchy.Two;
import org.springframework.core.ResolvableType;
import org.springframework.core.test.io.support.MockSpringFactoriesLoader;
import org.springframework.core.test.tools.CompileWithForkedClassLoader;
@@ -56,6 +60,7 @@ import org.springframework.core.test.tools.TestCompiler;
import org.springframework.javapoet.CodeBlock;
import org.springframework.javapoet.MethodSpec;
import org.springframework.javapoet.ParameterizedTypeName;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
@@ -89,8 +94,10 @@ class BeanDefinitionMethodGeneratorTests {
@Test
void generateBeanDefinitionMethodGeneratesMethod() {
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class));
void generateBeanDefinitionMethodWithOnlyTargetTypeDoesNotSetBeanClass() {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setTargetType(TestBean.class);
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
@@ -99,7 +106,67 @@ class BeanDefinitionMethodGeneratorTests {
compile(method, (actual, compiled) -> {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains("beanType = TestBean.class");
assertThat(sourceFile).contains("new RootBeanDefinition()");
assertThat(sourceFile).contains("setTargetType(TestBean.class)");
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
}
@Test
void generateBeanDefinitionMethodSpecifiesBeanClassIfSet() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(TestBean.class);
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
this.generationContext, this.beanRegistrationsCode);
compile(method, (actual, compiled) -> {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains("new RootBeanDefinition(TestBean.class)");
assertThat(sourceFile).doesNotContain("setTargetType(");
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
}
@Test
void generateBeanDefinitionMethodSpecifiesBeanClassAndTargetTypIfDifferent() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
beanDefinition.setTargetType(Implementation.class);
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
this.generationContext, this.beanRegistrationsCode);
compile(method, (actual, compiled) -> {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains("new RootBeanDefinition(TestHierarchy.One.class)");
assertThat(sourceFile).contains("setTargetType(TestHierarchy.Implementation.class)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
}
@Test
void generateBeanDefinitionMethodUSeBeanClassNameIfNotReachable() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(PackagePrivateTestBean.class);
beanDefinition.setTargetType(TestBean.class);
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
this.generationContext, this.beanRegistrationsCode);
compile(method, (actual, compiled) -> {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains("new RootBeanDefinition(\"org.springframework.beans.factory.aot.PackagePrivateTestBean\"");
assertThat(sourceFile).contains("setTargetType(TestBean.class)");
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
@@ -116,7 +183,6 @@ class BeanDefinitionMethodGeneratorTests {
compile(method, (actual, compiled) -> {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains("beanType = TestBean.class");
assertThat(sourceFile).contains("setInstanceSupplier(TestBean::new)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
@@ -183,12 +249,26 @@ class BeanDefinitionMethodGeneratorTests {
SourceFile sourceFile = compiled.getSourceFile(".*BeanDefinitions");
assertThat(sourceFile).contains("Get the bean definition for 'testBean'");
assertThat(sourceFile).contains(
"beanType = ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class)");
"setTargetType(ResolvableType.forClassWithGenerics(GenericBean.class, Integer.class))");
assertThat(sourceFile).contains("setInstanceSupplier(GenericBean::new)");
assertThat(actual).isInstanceOf(RootBeanDefinition.class);
});
}
@Test
void generateBeanDefinitionMethodWhenHasExplicitResolvableType() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(One.class);
beanDefinition.setResolvedFactoryMethod(ReflectionUtils.findMethod(TestHierarchy.class, "oneBean"));
beanDefinition.setTargetType(Two.class);
RegisteredBean registeredBean = registerBean(beanDefinition);
BeanDefinitionMethodGenerator generator = new BeanDefinitionMethodGenerator(
this.methodGeneratorFactory, registeredBean, null,
Collections.emptyList());
MethodReference method = generator.generateBeanDefinitionMethod(
this.generationContext, this.beanRegistrationsCode);
compile(method, (actual, compiled) -> assertThat(actual.getResolvableType().resolve()).isEqualTo(Two.class));
}
@Test
void generateBeanDefinitionMethodWhenHasInstancePostProcessorGeneratesMethod() {
RegisteredBean registeredBean = registerBean(new RootBeanDefinition(TestBean.class));