Restore lenient target type handling for FactoryBean definitions

Closes gh-23561
This commit is contained in:
Juergen Hoeller 2019-09-20 21:54:32 +02:00
parent a48c13ae73
commit e68132686d
5 changed files with 77 additions and 18 deletions

View File

@ -16,7 +16,6 @@
package org.springframework.beans.factory; package org.springframework.beans.factory;
import org.springframework.core.AttributeAccessor;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -61,12 +60,13 @@ public interface FactoryBean<T> {
/** /**
* The name of an attribute that can be * The name of an attribute that can be
* {@link AttributeAccessor#setAttribute set} on a * {@link org.springframework.core.AttributeAccessor#setAttribute set} on a
* {@link org.springframework.beans.factory.config.BeanDefinition} so that * {@link org.springframework.beans.factory.config.BeanDefinition} so that
* factory beans can signal their object type when it can't be deduced from * factory beans can signal their object type when it can't be deduced from
* the factory bean class. * the factory bean class.
* @since 5.2
*/ */
public static final String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/** /**

View File

@ -830,9 +830,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
return result; return result;
} }
ResolvableType beanType = mbd.hasBeanClass() ? ResolvableType beanType =
ResolvableType.forClass(mbd.getBeanClass()) : (mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : ResolvableType.NONE);
ResolvableType.NONE;
// For instance supplied beans try the target type and bean class // For instance supplied beans try the target type and bean class
if (mbd.getInstanceSupplier() != null) { if (mbd.getInstanceSupplier() != null) {
@ -2028,8 +2027,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
} }
private boolean isFactoryBeanMethod(Method method) { private boolean isFactoryBeanMethod(Method method) {
return method.getName().equals(this.factoryMethodName) && return (method.getName().equals(this.factoryMethodName) &&
FactoryBean.class.isAssignableFrom(method.getReturnType()); FactoryBean.class.isAssignableFrom(method.getReturnType()));
} }
ResolvableType getResult() { ResolvableType getResult() {

View File

@ -1603,7 +1603,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
Boolean result = mbd.isFactoryBean; Boolean result = mbd.isFactoryBean;
if (result == null) { if (result == null) {
Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class); Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
result = beanType != null && FactoryBean.class.isAssignableFrom(beanType); result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
mbd.isFactoryBean = result; mbd.isFactoryBean = result;
} }
return result; return result;
@ -1787,17 +1787,24 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
if (!(beanInstance instanceof FactoryBean)) { if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
} }
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
} }
// Now we have the bean instance, which may be a normal bean or a FactoryBean. // Now we have the bean instance, which may be a normal bean or a FactoryBean.
// If it's a FactoryBean, we use it to create a bean instance, unless the // If it's a FactoryBean, we use it to create a bean instance, unless the
// caller actually wants a reference to the factory. // caller actually wants a reference to the factory.
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { if (!(beanInstance instanceof FactoryBean)) {
return beanInstance; return beanInstance;
} }
Object object = null; Object object = null;
if (mbd == null) { if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
object = getCachedObjectForFactoryBean(beanName); object = getCachedObjectForFactoryBean(beanName);
} }
if (object == null) { if (object == null) {

View File

@ -332,18 +332,18 @@ public class AnnotationConfigApplicationContextTests {
@Test @Test
public void individualBeanWithFactoryBeanSupplier() { public void individualBeanWithFactoryBeanSupplier() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean("fb", TypedFactoryBean.class, TypedFactoryBean::new, bd -> bd.setLazyInit(true)); context.registerBean("fb", NonInstantiatedFactoryBean.class, NonInstantiatedFactoryBean::new, bd -> bd.setLazyInit(true));
context.refresh(); context.refresh();
assertThat(context.getType("fb")).isEqualTo(String.class); assertThat(context.getType("fb")).isEqualTo(String.class);
assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class); assertThat(context.getType("&fb")).isEqualTo(NonInstantiatedFactoryBean.class);
} }
@Test @Test
public void individualBeanWithFactoryBeanSupplierAndTargetType() { public void individualBeanWithFactoryBeanSupplierAndTargetType() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition(); RootBeanDefinition bd = new RootBeanDefinition();
bd.setInstanceSupplier(TypedFactoryBean::new); bd.setInstanceSupplier(NonInstantiatedFactoryBean::new);
bd.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class)); bd.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class));
bd.setLazyInit(true); bd.setLazyInit(true);
context.registerBeanDefinition("fb", bd); context.registerBeanDefinition("fb", bd);
@ -351,6 +351,42 @@ public class AnnotationConfigApplicationContextTests {
assertThat(context.getType("fb")).isEqualTo(String.class); assertThat(context.getType("fb")).isEqualTo(String.class);
assertThat(context.getType("&fb")).isEqualTo(FactoryBean.class); assertThat(context.getType("&fb")).isEqualTo(FactoryBean.class);
assertThat(context.getBeanNamesForType(FactoryBean.class).length).isEqualTo(1);
assertThat(context.getBeanNamesForType(NonInstantiatedFactoryBean.class).length).isEqualTo(0);
}
@Test
public void individualBeanWithFactoryBeanObjectTypeAsTargetType() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition();
bd.setBeanClass(TypedFactoryBean.class);
bd.setTargetType(String.class);
context.registerBeanDefinition("fb", bd);
context.refresh();
assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class);
assertThat(context.getType("fb")).isEqualTo(String.class);
assertThat(context.getBeanNamesForType(FactoryBean.class).length).isEqualTo(1);
assertThat(context.getBeanNamesForType(TypedFactoryBean.class).length).isEqualTo(1);
}
@Test
public void individualBeanWithFactoryBeanObjectTypeAsTargetTypeAndLazy() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition bd = new RootBeanDefinition();
bd.setBeanClass(TypedFactoryBean.class);
bd.setTargetType(String.class);
bd.setLazyInit(true);
context.registerBeanDefinition("fb", bd);
context.refresh();
assertThat(context.getType("&fb")).isNull();
assertThat(context.getType("fb")).isEqualTo(String.class);
assertThat(context.getBean("&fb") instanceof FactoryBean);
assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class);
assertThat(context.getType("fb")).isEqualTo(String.class);
assertThat(context.getBeanNamesForType(FactoryBean.class).length).isEqualTo(1);
assertThat(context.getBeanNamesForType(TypedFactoryBean.class).length).isEqualTo(1);
} }
@ -426,9 +462,9 @@ public class AnnotationConfigApplicationContextTests {
static class BeanC {} static class BeanC {}
static class TypedFactoryBean implements FactoryBean<String> { static class NonInstantiatedFactoryBean implements FactoryBean<String> {
public TypedFactoryBean() { public NonInstantiatedFactoryBean() {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@ -448,6 +484,24 @@ public class AnnotationConfigApplicationContextTests {
} }
} }
static class TypedFactoryBean implements FactoryBean<String> {
@Override
public String getObject() {
return "";
}
@Override
public Class<?> getObjectType() {
return String.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
static class UntypedFactoryBean implements FactoryBean<Object> { static class UntypedFactoryBean implements FactoryBean<Object> {
@Override @Override

View File

@ -194,8 +194,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests {
static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar { static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar {
@Override @Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
BeanDefinitionRegistry registry) {
BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition( BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(
RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition(); RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition();
definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class); definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);