Skip to content

Commit 89d150d

Browse files
committed
Resolve factoryBeanClass if necessary
Update `AbstractAutowireCapableBeanFactory.getTypeForFactoryBean` to use fallback to `determineTargetType` if the factory bean definition does not have a resolved class. This is required for the case where a `@Configuration` class is picked up via component scanning and has a bean type that has not yet been resolved. Closes gh-23338
1 parent 6eca9e7 commit 89d150d

File tree

2 files changed

+48
-8
lines changed

2 files changed

+48
-8
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -858,9 +858,16 @@ protected ResolvableType getTypeForFactoryBean(String beanName,
858858
// Try to obtain the FactoryBean's object type from its factory method
859859
// declaration without instantiating the containing bean at all.
860860
BeanDefinition factoryBeanDefinition = getBeanDefinition(factoryBeanName);
861-
if (factoryBeanDefinition instanceof AbstractBeanDefinition &&
862-
((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {
863-
Class<?> factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();
861+
Class<?> factoryBeanClass = null;
862+
if (factoryBeanDefinition instanceof AbstractBeanDefinition
863+
&& ((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {
864+
factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();
865+
}
866+
else {
867+
RootBeanDefinition fbmbd = getMergedBeanDefinition(factoryBeanName, factoryBeanDefinition);
868+
factoryBeanClass = determineTargetType(factoryBeanName, fbmbd, new Class<?>[] { Object.class });
869+
}
870+
if (factoryBeanClass != null) {
864871
result = getTypeForFactoryBeanFromMethod(factoryBeanClass, factoryMethodName);
865872
if (result.resolve() != null) {
866873
return result;

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import org.springframework.beans.factory.support.AbstractBeanFactory;
2929
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3030
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
31+
import org.springframework.beans.factory.support.GenericBeanDefinition;
32+
import org.springframework.context.support.GenericApplicationContext;
3133
import org.springframework.core.type.AnnotationMetadata;
3234

3335
import static org.assertj.core.api.Assertions.assertThat;
@@ -80,6 +82,32 @@ public void postFreezeAttribute() {
8082
assertPostFreeze(AttributeClassConfiguration.class);
8183
}
8284

85+
@Test
86+
public void preFreezeUnresolvedGenericFactoryBean() {
87+
// Covers the case where a @Configuration is picked up via component scanning
88+
// and its bean definition only has a String bean class. In such cases
89+
// beanDefinition.hasBeanClass() returns false so we need to actually
90+
// call determineTargetType ourselves
91+
GenericBeanDefinition factoryBeanDefinition = new GenericBeanDefinition();
92+
factoryBeanDefinition.setBeanClassName(GenericClassConfiguration.class.getName());
93+
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
94+
beanDefinition.setBeanClass(FactoryBean.class);
95+
beanDefinition.setFactoryBeanName("factoryBean");
96+
beanDefinition.setFactoryMethodName("myBean");
97+
GenericApplicationContext context = new GenericApplicationContext();
98+
try {
99+
context.registerBeanDefinition("factoryBean", factoryBeanDefinition);
100+
context.registerBeanDefinition("myBean", beanDefinition);
101+
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
102+
context.addBeanFactoryPostProcessor(postProcessor);
103+
context.refresh();
104+
assertContainsMyBeanName(postProcessor.getNames());
105+
}
106+
finally {
107+
context.close();
108+
}
109+
}
110+
83111
private void assertPostFreeze(Class<?> configurationClass) {
84112
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
85113
configurationClass);
@@ -90,11 +118,16 @@ private void assertPreFreeze(Class<?> configurationClass,
90118
BeanFactoryPostProcessor... postProcessors) {
91119
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
92120
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
93-
Arrays.stream(postProcessors).forEach(context::addBeanFactoryPostProcessor);
94-
context.addBeanFactoryPostProcessor(postProcessor);
95-
context.register(configurationClass);
96-
context.refresh();
97-
assertContainsMyBeanName(postProcessor.getNames());
121+
try {
122+
Arrays.stream(postProcessors).forEach(context::addBeanFactoryPostProcessor);
123+
context.addBeanFactoryPostProcessor(postProcessor);
124+
context.register(configurationClass);
125+
context.refresh();
126+
assertContainsMyBeanName(postProcessor.getNames());
127+
}
128+
finally {
129+
context.close();
130+
}
98131
}
99132

100133
private void assertContainsMyBeanName(AnnotationConfigApplicationContext context) {

0 commit comments

Comments
 (0)