Support FactoryBean bean definition attributes
Update `getTypeForFactoryBean` detection so that a bean definition attribute can be used to supply the result. This commit allows projects such as Spring Data to provide the result that would be supplied by `getObjectType` early so that we don't need to initialize the `FactoryBean` unnecessarily. Closes gh-23338
This commit is contained in:
committed by
Juergen Hoeller
parent
a0e462581f
commit
71a5308c78
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -58,6 +59,16 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public interface FactoryBean<T> {
|
||||
|
||||
/**
|
||||
* The name of an attribute that can be
|
||||
* {@link AttributeAccessor#setAttribute set} on a
|
||||
* {@link org.springframework.beans.factory.config.BeanDefinition} so that
|
||||
* factory beans can signal their object type when it can't be deduced from
|
||||
* the factory bean class.
|
||||
*/
|
||||
public static final String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
|
||||
|
||||
|
||||
/**
|
||||
* Return an instance (possibly shared or independent) of the object
|
||||
* managed by this factory.
|
||||
|
||||
@@ -826,7 +826,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
protected ResolvableType getTypeForFactoryBean(String beanName,
|
||||
RootBeanDefinition mbd, boolean allowInit) {
|
||||
|
||||
ResolvableType result = ResolvableType.NONE;
|
||||
// Check it the the bean definition itself has defined the type with an attribute
|
||||
ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
|
||||
if (result != ResolvableType.NONE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ResolvableType beanType = mbd.hasBeanClass() ?
|
||||
ResolvableType.forClass(mbd.getBeanClass()) :
|
||||
|
||||
@@ -65,6 +65,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.core.DecoratingClassLoader;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.core.ResolvableType;
|
||||
@@ -1607,7 +1608,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* already. Implementations are only allowed to instantiate the factory bean if
|
||||
* {@code allowInit} is {@code true}, otherwise they should try to determine the
|
||||
* result through other means.
|
||||
* <p>If {@code allowInit} is {@code true}, the default implementation will create
|
||||
* <p>If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
|
||||
* and {@code allowInit} is {@code true}, the default implementation will create
|
||||
* the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
|
||||
* Subclasses are encouraged to optimize this, typically by inspecting the generic
|
||||
* signature of the factory bean class or the factory method that creates it. If
|
||||
@@ -1617,7 +1619,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
* fallback.
|
||||
* @param beanName the name of the bean
|
||||
* @param mbd the merged bean definition for the bean
|
||||
* @param allowInit if initialization of the bean is permitted
|
||||
* @param allowInit if initialization of the FactoryBean is permitted
|
||||
* @return the type for the bean if determinable, otherwise {@code ResolvableType.NONE}
|
||||
* @since 5.2
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
@@ -1626,6 +1628,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
protected ResolvableType getTypeForFactoryBean(String beanName,
|
||||
RootBeanDefinition mbd, boolean allowInit) {
|
||||
|
||||
ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
|
||||
if (result != ResolvableType.NONE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (allowInit && mbd.isSingleton()) {
|
||||
try {
|
||||
FactoryBean<?> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
|
||||
@@ -1648,6 +1655,25 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the bean type for a FactoryBean by inspecting its attributes for a
|
||||
* {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} value.
|
||||
* @param attributes the attributes to inspect
|
||||
* @return a {@link ResolvableType} extracted from the attributes or
|
||||
* {@code ResolvableType.NONE}
|
||||
* @since 5.2
|
||||
*/
|
||||
ResolvableType getTypeForFactoryBeanFromAttributes(AttributeAccessor attributes) {
|
||||
Object attribute = attributes.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
|
||||
if (attribute instanceof ResolvableType) {
|
||||
return (ResolvableType) attribute;
|
||||
}
|
||||
if (attribute instanceof Class) {
|
||||
return ResolvableType.forClass((Class<?>) attribute);
|
||||
}
|
||||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the bean type for the given FactoryBean definition, as far as possible.
|
||||
* Only called if there is no singleton instance registered for the target bean already.
|
||||
|
||||
@@ -22,9 +22,13 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
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.core.type.AnnotationMetadata;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -66,6 +70,16 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
assertPostFreeze(GenericClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFreezeAttribute() {
|
||||
assertPreFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFreezeAttribute() {
|
||||
assertPostFreeze(AttributeClassConfiguration.class);
|
||||
}
|
||||
|
||||
private void assertPostFreeze(Class<?> configurationClass) {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||
configurationClass);
|
||||
@@ -138,7 +152,29 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
|
||||
}
|
||||
|
||||
static class MyBean {
|
||||
@Configuration
|
||||
@Import(AttributeClassRegistrar.class)
|
||||
static class AttributeClassConfiguration {
|
||||
|
||||
}
|
||||
|
||||
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
|
||||
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);
|
||||
registry.registerBeanDefinition("myBean", definition);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract static class AbstractMyBean {
|
||||
}
|
||||
|
||||
static class MyBean extends AbstractMyBean {
|
||||
}
|
||||
|
||||
static class TestFactoryBean<T> implements FactoryBean<T> {
|
||||
@@ -169,4 +205,20 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
|
||||
|
||||
}
|
||||
|
||||
static class RawWithAbstractObjectTypeFactoryBean implements FactoryBean<Object> {
|
||||
|
||||
private final Object object = new MyBean();
|
||||
|
||||
@Override
|
||||
public Object getObject() throws Exception {
|
||||
return object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return MyBean.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user