Skip to content

Commit 24ebd15

Browse files
committed
Introspect FactoryBean class declaration if no early instantiation possible
Issue: SPR-15125 (cherry picked from commit 32fc855)
1 parent cce8471 commit 24ebd15

File tree

2 files changed

+73
-25
lines changed

2 files changed

+73
-25
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -773,33 +773,20 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
773773
*/
774774
@Override
775775
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
776-
class Holder { Class<?> value = null; }
777-
final Holder objectType = new Holder();
778776
String factoryBeanName = mbd.getFactoryBeanName();
779777
final String factoryMethodName = mbd.getFactoryMethodName();
780778

781779
if (factoryBeanName != null) {
782780
if (factoryMethodName != null) {
783781
// Try to obtain the FactoryBean's object type without instantiating it at all.
784782
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
785-
if (fbDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) fbDef).hasBeanClass()) {
786-
// CGLIB subclass methods hide generic parameters; look at the original user class.
787-
Class<?> fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass());
788-
// Find the given factory method, taking into account that in the case of
789-
// @Bean methods, there may be parameters present.
790-
ReflectionUtils.doWithMethods(fbClass,
791-
new ReflectionUtils.MethodCallback() {
792-
@Override
793-
public void doWith(Method method) {
794-
if (method.getName().equals(factoryMethodName) &&
795-
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
796-
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(
797-
method, FactoryBean.class);
798-
}
799-
}
800-
});
801-
if (objectType.value != null && Object.class != objectType.value) {
802-
return objectType.value;
783+
if (fbDef instanceof AbstractBeanDefinition) {
784+
AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
785+
if (afbDef.hasBeanClass()) {
786+
Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
787+
if (result != null) {
788+
return result;
789+
}
803790
}
804791
}
805792
}
@@ -817,9 +804,9 @@ public void doWith(Method method) {
817804

818805
if (fb != null) {
819806
// Try to obtain the FactoryBean's object type from this early stage of the instance.
820-
objectType.value = getTypeForFactoryBean(fb);
821-
if (objectType.value != null) {
822-
return objectType.value;
807+
Class<?> result = getTypeForFactoryBean(fb);
808+
if (result != null) {
809+
return result;
823810
}
824811
else {
825812
// No type found for shortcut FactoryBean instance:
@@ -828,9 +815,52 @@ public void doWith(Method method) {
828815
}
829816
}
830817

818+
if (factoryBeanName == null && mbd.hasBeanClass()) {
819+
// No early bean instantiation possible: determine FactoryBean's type from
820+
// static factory method signature or from class inheritance hierarchy...
821+
if (factoryMethodName != null) {
822+
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
823+
}
824+
else {
825+
return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
826+
}
827+
}
828+
831829
return null;
832830
}
833831

832+
/**
833+
* Introspect the factory method signatures on the given bean class,
834+
* trying to find a common {@code FactoryBean} object type declared there.
835+
* @param beanClass the bean class to find the factory method on
836+
* @param factoryMethodName the name of the factory method
837+
* @return the common {@code FactoryBean} object type, or {@code null} if none
838+
*/
839+
private Class<?> getTypeForFactoryBeanFromMethod(Class<?> beanClass, final String factoryMethodName) {
840+
class Holder { Class<?> value = null; }
841+
final Holder objectType = new Holder();
842+
843+
// CGLIB subclass methods hide generic parameters; look at the original user class.
844+
Class<?> fbClass = ClassUtils.getUserClass(beanClass);
845+
// Find the given factory method, taking into account that in the case of
846+
// @Bean methods, there may be parameters present.
847+
ReflectionUtils.doWithMethods(fbClass,
848+
new ReflectionUtils.MethodCallback() {
849+
@Override
850+
public void doWith(Method method) {
851+
if (method.getName().equals(factoryMethodName) &&
852+
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
853+
Class<?> currentType = GenericTypeResolver.resolveReturnTypeArgument(
854+
method, FactoryBean.class);
855+
if (currentType != null) {
856+
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
857+
}
858+
}
859+
}
860+
});
861+
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
862+
}
863+
834864
/**
835865
* Obtain a reference for early access to the specified bean,
836866
* typically for the purpose of resolving a circular reference.

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ public void testAnnotatedDefaultConstructor() {
22312231
assertNotNull(bf.getBean("annotatedBean"));
22322232
}
22332233

2234-
@Test @Ignore // SPR-15125
2234+
@Test // SPR-15125
22352235
public void testFactoryBeanSelfInjection() {
22362236
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
22372237
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -2243,6 +2243,20 @@ public void testFactoryBeanSelfInjection() {
22432243
assertSame(bf.getBean("annotatedBean"), bean.testBean);
22442244
}
22452245

2246+
@Test // SPR-15125
2247+
public void testFactoryBeanSelfInjectionViaFactoryMethod() {
2248+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
2249+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
2250+
bpp.setBeanFactory(bf);
2251+
bf.addBeanPostProcessor(bpp);
2252+
RootBeanDefinition bd = new RootBeanDefinition(SelfInjectingFactoryBean.class);
2253+
bd.setFactoryMethodName("create");
2254+
bf.registerBeanDefinition("annotatedBean", bd);
2255+
2256+
SelfInjectingFactoryBean bean = bf.getBean(SelfInjectingFactoryBean.class);
2257+
assertSame(bf.getBean("annotatedBean"), bean.testBean);
2258+
}
2259+
22462260

22472261
@Qualifier("integerRepo")
22482262
private Repository<?> integerRepositoryQualifierProvider;
@@ -3570,6 +3584,10 @@ public Class<?> getObjectType() {
35703584
public boolean isSingleton() {
35713585
return true;
35723586
}
3587+
3588+
public static SelfInjectingFactoryBean create() {
3589+
return new SelfInjectingFactoryBean();
3590+
}
35733591
}
35743592

35753593
}

0 commit comments

Comments
 (0)