diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java index 0c806cef7b9..c20df830a43 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/FactoryBean.java @@ -16,7 +16,6 @@ package org.springframework.beans.factory; -import org.springframework.core.AttributeAccessor; import org.springframework.lang.Nullable; /** @@ -61,12 +60,13 @@ public interface FactoryBean { /** * 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 * factory beans can signal their object type when it can't be deduced from * the factory bean class. + * @since 5.2 */ - public static final String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; + String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index b07a611dc48..35d0df235c2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -830,9 +830,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac return result; } - ResolvableType beanType = mbd.hasBeanClass() ? - ResolvableType.forClass(mbd.getBeanClass()) : - ResolvableType.NONE; + ResolvableType beanType = + (mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : ResolvableType.NONE); // For instance supplied beans try the target type and bean class if (mbd.getInstanceSupplier() != null) { @@ -2028,8 +2027,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac } private boolean isFactoryBeanMethod(Method method) { - return method.getName().equals(this.factoryMethodName) && - FactoryBean.class.isAssignableFrom(method.getReturnType()); + return (method.getName().equals(this.factoryMethodName) && + FactoryBean.class.isAssignableFrom(method.getReturnType())); } ResolvableType getResult() { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index d67a8e3f3de..29fe3a8c464 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -1603,7 +1603,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp Boolean result = mbd.isFactoryBean; if (result == null) { Class beanType = predictBeanType(beanName, mbd, FactoryBean.class); - result = beanType != null && FactoryBean.class.isAssignableFrom(beanType); + result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType)); mbd.isFactoryBean = result; } return result; @@ -1787,17 +1787,24 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp if (!(beanInstance instanceof FactoryBean)) { 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. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. - if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { + if (!(beanInstance instanceof FactoryBean)) { return beanInstance; } Object object = null; - if (mbd == null) { + if (mbd != null) { + mbd.isFactoryBean = true; + } + else { object = getCachedObjectForFactoryBean(beanName); } if (object == null) { diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java index 9f5fd8e2b78..b9b61f4fa74 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationConfigApplicationContextTests.java @@ -332,18 +332,18 @@ public class AnnotationConfigApplicationContextTests { @Test public void individualBeanWithFactoryBeanSupplier() { 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(); assertThat(context.getType("fb")).isEqualTo(String.class); - assertThat(context.getType("&fb")).isEqualTo(TypedFactoryBean.class); + assertThat(context.getType("&fb")).isEqualTo(NonInstantiatedFactoryBean.class); } @Test public void individualBeanWithFactoryBeanSupplierAndTargetType() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); RootBeanDefinition bd = new RootBeanDefinition(); - bd.setInstanceSupplier(TypedFactoryBean::new); + bd.setInstanceSupplier(NonInstantiatedFactoryBean::new); bd.setTargetType(ResolvableType.forClassWithGenerics(FactoryBean.class, String.class)); bd.setLazyInit(true); context.registerBeanDefinition("fb", bd); @@ -351,6 +351,42 @@ public class AnnotationConfigApplicationContextTests { assertThat(context.getType("fb")).isEqualTo(String.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 TypedFactoryBean implements FactoryBean { + static class NonInstantiatedFactoryBean implements FactoryBean { - public TypedFactoryBean() { + public NonInstantiatedFactoryBean() { throw new IllegalStateException(); } @@ -448,6 +484,24 @@ public class AnnotationConfigApplicationContextTests { } } + static class TypedFactoryBean implements FactoryBean { + + @Override + public String getObject() { + return ""; + } + + @Override + public Class getObjectType() { + return String.class; + } + + @Override + public boolean isSingleton() { + return true; + } + } + static class UntypedFactoryBean implements FactoryBean { @Override diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java index 5ddf367dfb0..833e357f006 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationWithFactoryBeanBeanEarlyDeductionTests.java @@ -194,8 +194,7 @@ public class ConfigurationWithFactoryBeanBeanEarlyDeductionTests { static class AttributeClassRegistrar implements ImportBeanDefinitionRegistrar { @Override - public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, - BeanDefinitionRegistry registry) { + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { BeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition( RawWithAbstractObjectTypeFactoryBean.class).getBeanDefinition(); definition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, MyBean.class);