Skip to content

Commit 5dcd287

Browse files
committed
Fixed type resolution for uninitialized factory-method declaration
Issue: SPR-11112
1 parent a3b022a commit 5dcd287

File tree

4 files changed

+119
-13
lines changed

4 files changed

+119
-13
lines changed

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -657,10 +657,10 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
657657

658658
// If all factory methods have the same return type, return that type.
659659
// Can't clearly figure out exact method due to type converting / autowiring!
660+
Class<?> commonType = null;
660661
boolean cache = false;
661662
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
662663
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
663-
Set<Class<?>> returnTypes = new HashSet<Class<?>>(1);
664664
for (Method factoryMethod : candidates) {
665665
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
666666
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
@@ -694,7 +694,7 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
694694
factoryMethod, args, getBeanClassLoader());
695695
if (returnType != null) {
696696
cache = true;
697-
returnTypes.add(returnType);
697+
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
698698
}
699699
}
700700
catch (Throwable ex) {
@@ -704,18 +704,17 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
704704
}
705705
}
706706
else {
707-
returnTypes.add(factoryMethod.getReturnType());
707+
commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType);
708708
}
709709
}
710710
}
711711

712-
if (returnTypes.size() == 1) {
712+
if (commonType != null) {
713713
// Clear return type found: all factory methods return same type.
714-
Class<?> result = returnTypes.iterator().next();
715714
if (cache) {
716-
mbd.resolvedFactoryMethodReturnType = result;
715+
mbd.resolvedFactoryMethodReturnType = commonType;
717716
}
718-
return result;
717+
return commonType;
719718
}
720719
else {
721720
// Ambiguous return types found: return null to indicate "not determinable".

spring-beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethods.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ protected static FactoryMethods newInstance(TestBean tb, int num, String name) {
5454
return new FactoryMethods(tb, name, num);
5555
}
5656

57-
static FactoryMethods newInstance(TestBean tb, int num, Integer something) {
57+
static ExtendedFactoryMethods newInstance(TestBean tb, int num, Integer something) {
5858
if (something != null) {
5959
throw new IllegalStateException("Should never be called with non-null value");
6060
}
61-
return new FactoryMethods(tb, null, num);
61+
return new ExtendedFactoryMethods(tb, null, num);
6262
}
6363

6464
@SuppressWarnings("unused")
@@ -119,4 +119,12 @@ public void setName(String name) {
119119
this.name = name;
120120
}
121121

122+
123+
public static class ExtendedFactoryMethods extends FactoryMethods {
124+
125+
ExtendedFactoryMethods(TestBean tb, String name, int num) {
126+
super(tb, name, num);
127+
}
128+
}
129+
122130
}

spring-core/src/main/java/org/springframework/util/ClassUtils.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,39 @@ public static Class<?> createCompositeInterface(Class<?>[] interfaces, ClassLoad
11431143
return Proxy.getProxyClass(classLoader, interfaces);
11441144
}
11451145

1146+
/**
1147+
* Determine the common ancestor of the given classes, if any.
1148+
* @param clazz1 the class to introspect
1149+
* @param clazz2 the other class to introspect
1150+
* @return the common ancestor (i.e. common superclass, one interface
1151+
* extending the other), or {@code null} if none found. If any of the
1152+
* given classes is {@code null}, the other class will be returned.
1153+
* @since 3.2.6
1154+
*/
1155+
public static Class<?> determineCommonAncestor(Class<?> clazz1, Class<?> clazz2) {
1156+
if (clazz1 == null) {
1157+
return clazz2;
1158+
}
1159+
if (clazz2 == null) {
1160+
return clazz1;
1161+
}
1162+
if (clazz1.isAssignableFrom(clazz2)) {
1163+
return clazz1;
1164+
}
1165+
if (clazz2.isAssignableFrom(clazz1)) {
1166+
return clazz2;
1167+
}
1168+
Class<?> ancestor = clazz1;
1169+
do {
1170+
ancestor = ancestor.getSuperclass();
1171+
if (ancestor == null || Object.class.equals(ancestor)) {
1172+
return null;
1173+
}
1174+
}
1175+
while (!ancestor.isAssignableFrom(clazz2));
1176+
return ancestor;
1177+
}
1178+
11461179
/**
11471180
* Check whether the given class is visible in the given ClassLoader.
11481181
* @param clazz the class to check (typically an interface)

0 commit comments

Comments
 (0)