Skip to content

Commit 32fc855

Browse files
committed
Introspect FactoryBean class declaration if no early instantiation possible
Issue: SPR-15125
1 parent e88e8f1 commit 32fc855

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-2018 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.
@@ -786,33 +786,20 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
786786
*/
787787
@Override
788788
protected Class<?> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
789-
class Holder { Class<?> value = null; }
790-
final Holder objectType = new Holder();
791789
String factoryBeanName = mbd.getFactoryBeanName();
792790
final String factoryMethodName = mbd.getFactoryMethodName();
793791

794792
if (factoryBeanName != null) {
795793
if (factoryMethodName != null) {
796794
// Try to obtain the FactoryBean's object type without instantiating it at all.
797795
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
798-
if (fbDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) fbDef).hasBeanClass()) {
799-
// CGLIB subclass methods hide generic parameters; look at the original user class.
800-
Class<?> fbClass = ClassUtils.getUserClass(((AbstractBeanDefinition) fbDef).getBeanClass());
801-
// Find the given factory method, taking into account that in the case of
802-
// @Bean methods, there may be parameters present.
803-
ReflectionUtils.doWithMethods(fbClass,
804-
new ReflectionUtils.MethodCallback() {
805-
@Override
806-
public void doWith(Method method) {
807-
if (method.getName().equals(factoryMethodName) &&
808-
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
809-
objectType.value = GenericTypeResolver.resolveReturnTypeArgument(
810-
method, FactoryBean.class);
811-
}
812-
}
813-
});
814-
if (objectType.value != null && Object.class != objectType.value) {
815-
return objectType.value;
796+
if (fbDef instanceof AbstractBeanDefinition) {
797+
AbstractBeanDefinition afbDef = (AbstractBeanDefinition) fbDef;
798+
if (afbDef.hasBeanClass()) {
799+
Class<?> result = getTypeForFactoryBeanFromMethod(afbDef.getBeanClass(), factoryMethodName);
800+
if (result != null) {
801+
return result;
802+
}
816803
}
817804
}
818805
}
@@ -830,9 +817,9 @@ public void doWith(Method method) {
830817

831818
if (fb != null) {
832819
// Try to obtain the FactoryBean's object type from this early stage of the instance.
833-
objectType.value = getTypeForFactoryBean(fb);
834-
if (objectType.value != null) {
835-
return objectType.value;
820+
Class<?> result = getTypeForFactoryBean(fb);
821+
if (result != null) {
822+
return result;
836823
}
837824
else {
838825
// No type found for shortcut FactoryBean instance:
@@ -841,9 +828,52 @@ public void doWith(Method method) {
841828
}
842829
}
843830

831+
if (factoryBeanName == null && mbd.hasBeanClass()) {
832+
// No early bean instantiation possible: determine FactoryBean's type from
833+
// static factory method signature or from class inheritance hierarchy...
834+
if (factoryMethodName != null) {
835+
return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
836+
}
837+
else {
838+
return GenericTypeResolver.resolveTypeArgument(mbd.getBeanClass(), FactoryBean.class);
839+
}
840+
}
841+
844842
return null;
845843
}
846844

845+
/**
846+
* Introspect the factory method signatures on the given bean class,
847+
* trying to find a common {@code FactoryBean} object type declared there.
848+
* @param beanClass the bean class to find the factory method on
849+
* @param factoryMethodName the name of the factory method
850+
* @return the common {@code FactoryBean} object type, or {@code null} if none
851+
*/
852+
private Class<?> getTypeForFactoryBeanFromMethod(Class<?> beanClass, String factoryMethodName) {
853+
class Holder { Class<?> value = null; }
854+
final Holder objectType = new Holder();
855+
856+
// CGLIB subclass methods hide generic parameters; look at the original user class.
857+
Class<?> fbClass = ClassUtils.getUserClass(beanClass);
858+
// Find the given factory method, taking into account that in the case of
859+
// @Bean methods, there may be parameters present.
860+
ReflectionUtils.doWithMethods(fbClass,
861+
new ReflectionUtils.MethodCallback() {
862+
@Override
863+
public void doWith(Method method) {
864+
if (method.getName().equals(factoryMethodName) &&
865+
FactoryBean.class.isAssignableFrom(method.getReturnType())) {
866+
Class<?> currentType = GenericTypeResolver.resolveReturnTypeArgument(
867+
method, FactoryBean.class);
868+
if (currentType != null) {
869+
objectType.value = ClassUtils.determineCommonAncestor(currentType, objectType.value);
870+
}
871+
}
872+
}
873+
});
874+
return (objectType.value != null && Object.class != objectType.value ? objectType.value : null);
875+
}
876+
847877
/**
848878
* Obtain a reference for early access to the specified bean,
849879
* 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
@@ -2237,7 +2237,7 @@ public void testAnnotatedDefaultConstructor() {
22372237
assertNotNull(bf.getBean("annotatedBean"));
22382238
}
22392239

2240-
@Test @Ignore // SPR-15125
2240+
@Test // SPR-15125
22412241
public void testFactoryBeanSelfInjection() {
22422242
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
22432243
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -2249,6 +2249,20 @@ public void testFactoryBeanSelfInjection() {
22492249
assertSame(bf.getBean("annotatedBean"), bean.testBean);
22502250
}
22512251

2252+
@Test // SPR-15125
2253+
public void testFactoryBeanSelfInjectionViaFactoryMethod() {
2254+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
2255+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
2256+
bpp.setBeanFactory(bf);
2257+
bf.addBeanPostProcessor(bpp);
2258+
RootBeanDefinition bd = new RootBeanDefinition(SelfInjectingFactoryBean.class);
2259+
bd.setFactoryMethodName("create");
2260+
bf.registerBeanDefinition("annotatedBean", bd);
2261+
2262+
SelfInjectingFactoryBean bean = bf.getBean(SelfInjectingFactoryBean.class);
2263+
assertSame(bf.getBean("annotatedBean"), bean.testBean);
2264+
}
2265+
22522266

22532267
@Qualifier("integerRepo")
22542268
private Repository<?> integerRepositoryQualifierProvider;
@@ -3586,6 +3600,10 @@ public Class<?> getObjectType() {
35863600
public boolean isSingleton() {
35873601
return true;
35883602
}
3603+
3604+
public static SelfInjectingFactoryBean create() {
3605+
return new SelfInjectingFactoryBean();
3606+
}
35893607
}
35903608

35913609
}

0 commit comments

Comments
 (0)