Detect target of factory method with AOT
Previously, if a factory method is defined on a parent, the generated code would blindly use the method's declaring class for both the target of the generated code, and the signature of the method. This commit improves the resolution by considering the factory metadata in the BeanDefinition. Closes gh-32609
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -161,7 +161,8 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
@Test
|
||||
void generateWithBeanClassAndFactoryMethodNameSetsTargetTypeAndBeanClass() {
|
||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
||||
this.beanFactory.registerBeanDefinition("factory",
|
||||
new RootBeanDefinition(SimpleBeanConfiguration.class));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("factory");
|
||||
beanDefinition.setFactoryMethodName("simpleBean");
|
||||
@@ -182,7 +183,8 @@ class BeanDefinitionMethodGeneratorTests {
|
||||
|
||||
@Test
|
||||
void generateWithTargetTypeAndFactoryMethodNameSetsOnlyBeanClass() {
|
||||
this.beanFactory.registerSingleton("factory", new SimpleBeanConfiguration());
|
||||
this.beanFactory.registerBeanDefinition("factory",
|
||||
new RootBeanDefinition(SimpleBeanConfiguration.class));
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition();
|
||||
beanDefinition.setTargetType(SimpleBean.class);
|
||||
beanDefinition.setFactoryBeanName("factory");
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.factory.DummyFactory;
|
||||
import org.springframework.beans.testfixture.beans.factory.StringFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.DefaultSimpleBeanContract;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.GenericFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationCode;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.MockBeanRegistrationsCode;
|
||||
@@ -38,6 +39,7 @@ import org.springframework.beans.testfixture.beans.factory.aot.NumberFactoryBean
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanArrayFactoryBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanContract;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanFactoryBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
@@ -126,6 +128,21 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
||||
SimpleBeanConfiguration.class);
|
||||
}
|
||||
|
||||
@Test // gh-32609
|
||||
void getTargetOnMethodFromInterface() {
|
||||
this.beanFactory.registerBeanDefinition("configuration",
|
||||
new RootBeanDefinition(DefaultSimpleBeanContract.class));
|
||||
Method method = ReflectionUtils.findMethod(SimpleBeanContract.class, "simpleBean");
|
||||
assertThat(method).isNotNull();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(SimpleBean.class);
|
||||
applyConstructorOrFactoryMethod(beanDefinition, method);
|
||||
beanDefinition.setFactoryBeanName("configuration");
|
||||
this.beanFactory.registerBeanDefinition("testBean", beanDefinition);
|
||||
RegisteredBean registeredBean = RegisteredBean.of(this.beanFactory, "testBean");
|
||||
assertTarget(createInstance(registeredBean).getTarget(registeredBean),
|
||||
DefaultSimpleBeanContract.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getTargetOnMethodWithInnerBeanInJavaPackage() {
|
||||
RegisteredBean registeredBean = registerTestBean(SimpleBean.class);
|
||||
@@ -190,7 +207,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizedGetTargetDoesNotResolveConstructorOrFactoryMethod() {
|
||||
void customizedGetTargetDoesNotResolveInstantiationDescriptor() {
|
||||
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
||||
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
||||
@Override
|
||||
@@ -199,11 +216,11 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
||||
}
|
||||
});
|
||||
assertTarget(customCodeFragments.getTarget(registeredBean), String.class);
|
||||
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
|
||||
verify(registeredBean, never()).resolveInstantiationDescriptor();
|
||||
}
|
||||
|
||||
@Test
|
||||
void customizedGenerateInstanceSupplierCodeDoesNotResolveConstructorOrFactoryMethod() {
|
||||
void customizedGenerateInstanceSupplierCodeDoesNotResolveInstantiationDescriptor() {
|
||||
RegisteredBean registeredBean = spy(registerTestBean(SimpleBean.class));
|
||||
BeanRegistrationCodeFragments customCodeFragments = createCustomCodeFragments(registeredBean, codeFragments -> new BeanRegistrationCodeFragmentsDecorator(codeFragments) {
|
||||
@Override
|
||||
@@ -214,7 +231,7 @@ class DefaultBeanRegistrationCodeFragmentsTests {
|
||||
});
|
||||
assertThat(customCodeFragments.generateInstanceSupplierCode(this.generationContext,
|
||||
new MockBeanRegistrationCode(this.generationContext), false)).hasToString("// Hello");
|
||||
verify(registeredBean, never()).resolveConstructorOrFactoryMethod();
|
||||
verify(registeredBean, never()).resolveInstantiationDescriptor();
|
||||
}
|
||||
|
||||
private BeanRegistrationCodeFragments createCustomCodeFragments(RegisteredBean registeredBean, UnaryOperator<BeanRegistrationCodeFragments> customFragments) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.aot;
|
||||
|
||||
import java.lang.reflect.Executable;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -38,10 +37,14 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.InstanceSupplier;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RegisteredBean.InstantiationDescriptor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.beans.testfixture.beans.TestBeanWithPrivateConstructor;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.DefaultSimpleBeanContract;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.DeferredTypeBuilder;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBean;
|
||||
import org.springframework.beans.testfixture.beans.factory.aot.SimpleBeanContract;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.EnvironmentAwareComponent;
|
||||
import org.springframework.beans.testfixture.beans.factory.generator.InnerComponentConfiguration.NoDependencyComponent;
|
||||
@@ -185,6 +188,23 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasFactoryMethodOnInterface() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SimpleBean.class)
|
||||
.setFactoryMethodOnBean("simpleBean", "config").getBeanDefinition();
|
||||
this.beanFactory.registerBeanDefinition("config", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DefaultSimpleBeanContract.class).getBeanDefinition());
|
||||
compile(beanDefinition, (instanceSupplier, compiled) -> {
|
||||
Object bean = getBean(beanDefinition, instanceSupplier);
|
||||
assertThat(bean).isInstanceOf(SimpleBean.class);
|
||||
assertThat(compiled.getSourceFile()).contains(
|
||||
"getBeanFactory().getBean(DefaultSimpleBeanContract.class).simpleBean()");
|
||||
});
|
||||
assertThat(getReflectionHints().getTypeHint(SimpleBeanContract.class))
|
||||
.satisfies(hasMethodWithMode(ExecutableMode.INTROSPECT));
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateWhenHasPrivateStaticFactoryMethodWithNoArg() {
|
||||
BeanDefinition beanDefinition = BeanDefinitionBuilder
|
||||
@@ -402,9 +422,9 @@ class InstanceSupplierCodeGeneratorTests {
|
||||
InstanceSupplierCodeGenerator generator = new InstanceSupplierCodeGenerator(
|
||||
this.generationContext, generateClass.getName(),
|
||||
generateClass.getMethods(), false);
|
||||
Executable constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod();
|
||||
assertThat(constructorOrFactoryMethod).isNotNull();
|
||||
CodeBlock generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod);
|
||||
InstantiationDescriptor instantiationDescriptor = registeredBean.resolveInstantiationDescriptor();
|
||||
assertThat(instantiationDescriptor).isNotNull();
|
||||
CodeBlock generatedCode = generator.generateCode(registeredBean, instantiationDescriptor);
|
||||
typeBuilder.set(type -> {
|
||||
type.addModifiers(Modifier.PUBLIC);
|
||||
type.addSuperinterface(ParameterizedTypeName.get(Supplier.class, InstanceSupplier.class));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -116,9 +116,9 @@ class InstanceSupplierCodeGeneratorKotlinTests {
|
||||
generationContext, generateClass.name,
|
||||
generateClass.methods, false
|
||||
)
|
||||
val constructorOrFactoryMethod = registeredBean.resolveConstructorOrFactoryMethod()
|
||||
Assertions.assertThat(constructorOrFactoryMethod).isNotNull()
|
||||
val generatedCode = generator.generateCode(registeredBean, constructorOrFactoryMethod)
|
||||
val instantiationDescriptor = registeredBean.resolveInstantiationDescriptor()
|
||||
Assertions.assertThat(instantiationDescriptor).isNotNull()
|
||||
val generatedCode = generator.generateCode(registeredBean, instantiationDescriptor)
|
||||
typeBuilder.set { type: TypeSpec.Builder ->
|
||||
type.addModifiers(Modifier.PUBLIC)
|
||||
type.addSuperinterface(
|
||||
|
||||
Reference in New Issue
Block a user