Browse Source

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
pull/23394/head
Phillip Webb 6 years ago
parent
commit
89d150d398
  1. 13
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  2. 43
      spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java

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

@ -858,9 +858,16 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac @@ -858,9 +858,16 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
// Try to obtain the FactoryBean's object type from its factory method
// declaration without instantiating the containing bean at all.
BeanDefinition factoryBeanDefinition = getBeanDefinition(factoryBeanName);
if (factoryBeanDefinition instanceof AbstractBeanDefinition &&
((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {
Class<?> factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();
Class<?> factoryBeanClass = null;
if (factoryBeanDefinition instanceof AbstractBeanDefinition
&& ((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {
factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();
}
else {
RootBeanDefinition fbmbd = getMergedBeanDefinition(factoryBeanName, factoryBeanDefinition);
factoryBeanClass = determineTargetType(factoryBeanName, fbmbd, new Class<?>[] { Object.class });
}
if (factoryBeanClass != null) {
result = getTypeForFactoryBeanFromMethod(factoryBeanClass, factoryMethodName);
if (result.resolve() != null) {
return result;

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

@ -28,6 +28,8 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -28,6 +28,8 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.type.AnnotationMetadata;
import static org.assertj.core.api.Assertions.assertThat;
@ -80,6 +82,32 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { @@ -80,6 +82,32 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
assertPostFreeze(AttributeClassConfiguration.class);
}
@Test
public void preFreezeUnresolvedGenericFactoryBean() {
// Covers the case where a @Configuration is picked up via component scanning
// and its bean definition only has a String bean class. In such cases
// beanDefinition.hasBeanClass() returns false so we need to actually
// call determineTargetType ourselves
GenericBeanDefinition factoryBeanDefinition = new GenericBeanDefinition();
factoryBeanDefinition.setBeanClassName(GenericClassConfiguration.class.getName());
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(FactoryBean.class);
beanDefinition.setFactoryBeanName("factoryBean");
beanDefinition.setFactoryMethodName("myBean");
GenericApplicationContext context = new GenericApplicationContext();
try {
context.registerBeanDefinition("factoryBean", factoryBeanDefinition);
context.registerBeanDefinition("myBean", beanDefinition);
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
context.addBeanFactoryPostProcessor(postProcessor);
context.refresh();
assertContainsMyBeanName(postProcessor.getNames());
}
finally {
context.close();
}
}
private void assertPostFreeze(Class<?> configurationClass) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
configurationClass);
@ -90,11 +118,16 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { @@ -90,11 +118,16 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
BeanFactoryPostProcessor... postProcessors) {
NameCollectingBeanFactoryPostProcessor postProcessor = new NameCollectingBeanFactoryPostProcessor();
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
Arrays.stream(postProcessors).forEach(context::addBeanFactoryPostProcessor);
context.addBeanFactoryPostProcessor(postProcessor);
context.register(configurationClass);
context.refresh();
assertContainsMyBeanName(postProcessor.getNames());
try {
Arrays.stream(postProcessors).forEach(context::addBeanFactoryPostProcessor);
context.addBeanFactoryPostProcessor(postProcessor);
context.register(configurationClass);
context.refresh();
assertContainsMyBeanName(postProcessor.getNames());
}
finally {
context.close();
}
}
private void assertContainsMyBeanName(AnnotationConfigApplicationContext context) {

Loading…
Cancel
Save