Restore lenient target type handling for FactoryBean definitions
Closes gh-23561
This commit is contained in:
parent
a48c13ae73
commit
e68132686d
|
|
@ -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";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue