Introspect FactoryBean class declaration if no early instantiation possible
Issue: SPR-15125
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2018 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -786,8 +786,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
|
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
|
||||||
class Holder { Class<?> value = null; }
|
|
||||||
final Holder objectType = new Holder();
|
|
||||||
String factoryBeanName = mbd.getFactoryBeanName();
|
String factoryBeanName = mbd.getFactoryBeanName();
|
||||||
final String factoryMethodName = mbd.getFactoryMethodName();
|
final String factoryMethodName = mbd.getFactoryMethodName();
|
||||||
|
|
||||||
@@ -795,24 +793,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
if (factoryMethodName != null) {
|
if (factoryMethodName != null) {
|
||||||
// Try to obtain the FactoryBean's object type without instantiating it at all.
|
// Try to obtain the FactoryBean's object type without instantiating it at all.
|
||||||
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
|
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
|
||||||
if (fbDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) fbDef).hasBeanClass()) {
|
if (fbDef instanceof AbstractBeanDefinition) {
|
||||||
// CGLIB subclass methods hide generic parameters; look at the original user class.
|
AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
|
||||||
Class<?> fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass());
|
if (afbDef.hasBeanClass()) {
|
||||||
// Find the given factory method, taking into account that in the case of
|
Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
|
||||||
// @Bean methods, there may be parameters present.
|
if (result != null) {
|
||||||
ReflectionUtils.doWithMethods(fbClass,
|
return result;
|
||||||
new ReflectionUtils.MethodCallback() {
|
}
|
||||||
@Override
|
|
||||||
public void doWith(Method method) {
|
|
||||||
if (method.getName().equals(factoryMethodName) &&
|
|
||||||
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
|
|
||||||
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(
|
|
||||||
method, FactoryBean.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (objectType.value != null && Object.class != objectType.value) {
|
|
||||||
return objectType.value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -830,9 +817,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
|
|
||||||
if (fb != null) {
|
if (fb != null) {
|
||||||
// Try to obtain the FactoryBean's object type from this early stage of the instance.
|
// Try to obtain the FactoryBean's object type from this early stage of the instance.
|
||||||
objectType.value = getTypeForFactoryBean(fb);
|
Class<?> result = getTypeForFactoryBean(fb);
|
||||||
if (objectType.value != null) {
|
if (result != null) {
|
||||||
return objectType.value;
|
return result;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No type found for shortcut FactoryBean instance:
|
// No type found for shortcut FactoryBean instance:
|
||||||
@@ -841,9 +828,52 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (factoryBeanName == null && mbd.hasBeanClass()) {
|
||||||
|
// No early bean instantiation possible: determine FactoryBean's type from
|
||||||
|
// static factory method signature or from class inheritance hierarchy...
|
||||||
|
if (factoryMethodName != null) {
|
||||||
|
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Introspect the factory method signatures on the given bean class,
|
||||||
|
* trying to find a common {@code FactoryBean} object type declared there.
|
||||||
|
* @param beanClass the bean class to find the factory method on
|
||||||
|
* @param factoryMethodName the name of the factory method
|
||||||
|
* @return the common {@code FactoryBean} object type, or {@code null} if none
|
||||||
|
*/
|
||||||
|
private Class<?> getTypeForFactoryBeanFromMethod(Class<?> beanClass, String factoryMethodName) {
|
||||||
|
class Holder { Class<?> value = null; }
|
||||||
|
final Holder objectType = new Holder();
|
||||||
|
|
||||||
|
// CGLIB subclass methods hide generic parameters; look at the original user class.
|
||||||
|
Class<?> fbClass = ClassUtils.getUserClass(beanClass);
|
||||||
|
// Find the given factory method, taking into account that in the case of
|
||||||
|
// @Bean methods, there may be parameters present.
|
||||||
|
ReflectionUtils.doWithMethods(fbClass,
|
||||||
|
new ReflectionUtils.MethodCallback() {
|
||||||
|
@Override
|
||||||
|
public void doWith(Method method) {
|
||||||
|
if (method.getName().equals(factoryMethodName) &&
|
||||||
|
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
|
||||||
|
Class<?> currentType = GenericTypeResolver.resolveReturnTypeArgument(
|
||||||
|
method, FactoryBean.class);
|
||||||
|
if (currentType != null) {
|
||||||
|
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a reference for early access to the specified bean,
|
* Obtain a reference for early access to the specified bean,
|
||||||
* typically for the purpose of resolving a circular reference.
|
* typically for the purpose of resolving a circular reference.
|
||||||
|
|||||||
@@ -2237,7 +2237,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertNotNull(bf.getBean("annotatedBean"));
|
assertNotNull(bf.getBean("annotatedBean"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test @Ignore // SPR-15125
|
@Test // SPR-15125
|
||||||
public void testFactoryBeanSelfInjection() {
|
public void testFactoryBeanSelfInjection() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
@@ -2249,6 +2249,20 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertSame(bf.getBean("annotatedBean"), bean.testBean);
|
assertSame(bf.getBean("annotatedBean"), bean.testBean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // SPR-15125
|
||||||
|
public void testFactoryBeanSelfInjectionViaFactoryMethod() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
bpp.setBeanFactory(bf);
|
||||||
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
RootBeanDefinition bd = new RootBeanDefinition(SelfInjectingFactoryBean.class);
|
||||||
|
bd.setFactoryMethodName("create");
|
||||||
|
bf.registerBeanDefinition("annotatedBean", bd);
|
||||||
|
|
||||||
|
SelfInjectingFactoryBean bean = bf.getBean(SelfInjectingFactoryBean.class);
|
||||||
|
assertSame(bf.getBean("annotatedBean"), bean.testBean);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Qualifier("integerRepo")
|
@Qualifier("integerRepo")
|
||||||
private Repository<?> integerRepositoryQualifierProvider;
|
private Repository<?> integerRepositoryQualifierProvider;
|
||||||
@@ -3586,6 +3600,10 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
public boolean isSingleton() {
|
public boolean isSingleton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SelfInjectingFactoryBean create() {
|
||||||
|
return new SelfInjectingFactoryBean();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user