Skip to content

Commit 8e52e65

Browse files
committed
Fixed type resolution for uninitialized factory-method declaration
Issue: SPR-11112 (cherry picked from commit 5dcd287)
1 parent 71650c0 commit 8e52e65

File tree

7 files changed

+151
-44
lines changed

7 files changed

+151
-44
lines changed

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

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -135,21 +135,21 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
135135
* Dependency types to ignore on dependency check and autowire, as Set of
136136
* Class objects: for example, String. Default is none.
137137
*/
138-
private final Set<Class> ignoredDependencyTypes = new HashSet<Class>();
138+
private final Set<Class<?>> ignoredDependencyTypes = new HashSet<Class<?>>();
139139

140140
/**
141141
* Dependency interfaces to ignore on dependency check and autowire, as Set of
142142
* Class objects. By default, only the BeanFactory interface is ignored.
143143
*/
144-
private final Set<Class> ignoredDependencyInterfaces = new HashSet<Class>();
144+
private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<Class<?>>();
145145

146146
/** Cache of unfinished FactoryBean instances: FactoryBean name --> BeanWrapper */
147147
private final Map<String, BeanWrapper> factoryBeanInstanceCache =
148148
new ConcurrentHashMap<String, BeanWrapper>(16);
149149

150150
/** Cache of filtered PropertyDescriptors: bean Class -> PropertyDescriptor array */
151-
private final Map<Class, PropertyDescriptor[]> filteredPropertyDescriptorsCache =
152-
new ConcurrentHashMap<Class, PropertyDescriptor[]>(64);
151+
private final Map<Class<?>, PropertyDescriptor[]> filteredPropertyDescriptorsCache =
152+
new ConcurrentHashMap<Class<?>, PropertyDescriptor[]>(64);
153153

154154

155155
/**
@@ -506,7 +506,7 @@ protected Object doCreateBean(final String beanName, final RootBeanDefinition mb
506506
logger.debug("Eagerly caching bean '" + beanName +
507507
"' to allow for resolving potential circular references");
508508
}
509-
addSingletonFactory(beanName, new ObjectFactory() {
509+
addSingletonFactory(beanName, new ObjectFactory<Object>() {
510510
public Object getObject() throws BeansException {
511511
return getEarlyBeanReference(beanName, mbd, bean);
512512
}
@@ -634,9 +634,9 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
634634

635635
// If all factory methods have the same return type, return that type.
636636
// Can't clearly figure out exact method due to type converting / autowiring!
637+
Class<?> commonType = null;
637638
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
638639
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
639-
Set<Class<?>> returnTypes = new HashSet<Class<?>>(1);
640640
for (Method factoryMethod : candidates) {
641641
if (Modifier.isStatic(factoryMethod.getModifiers()) == isStatic &&
642642
factoryMethod.getName().equals(mbd.getFactoryMethodName()) &&
@@ -669,7 +669,7 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
669669
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
670670
factoryMethod, args, getBeanClassLoader());
671671
if (returnType != null) {
672-
returnTypes.add(returnType);
672+
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
673673
}
674674
}
675675
catch (Throwable ex) {
@@ -679,14 +679,14 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
679679
}
680680
}
681681
else {
682-
returnTypes.add(factoryMethod.getReturnType());
682+
commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType);
683683
}
684684
}
685685
}
686686

687-
if (returnTypes.size() == 1) {
687+
if (commonType != null) {
688688
// Clear return type found: all factory methods return same type.
689-
return returnTypes.iterator().next();
689+
return commonType;
690690
}
691691
else {
692692
// Ambiguous return types found: return null to indicate "not determinable".
@@ -788,11 +788,11 @@ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd,
788788
* @return the FactoryBean instance, or {@code null} to indicate
789789
* that we couldn't obtain a shortcut FactoryBean instance
790790
*/
791-
private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
791+
private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
792792
synchronized (getSingletonMutex()) {
793793
BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
794794
if (bw != null) {
795-
return (FactoryBean) bw.getWrappedInstance();
795+
return (FactoryBean<?>) bw.getWrappedInstance();
796796
}
797797
if (isSingletonCurrentlyInCreation(beanName)) {
798798
return null;
@@ -812,7 +812,7 @@ private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, RootBea
812812
// Finished partial creation of this bean.
813813
afterSingletonCreation(beanName);
814814
}
815-
FactoryBean fb = getFactoryBean(beanName, instance);
815+
FactoryBean<?> fb = getFactoryBean(beanName, instance);
816816
if (bw != null) {
817817
this.factoryBeanInstanceCache.put(beanName, bw);
818818
}
@@ -829,7 +829,7 @@ private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, RootBea
829829
* @return the FactoryBean instance, or {@code null} to indicate
830830
* that we couldn't obtain a shortcut FactoryBean instance
831831
*/
832-
private FactoryBean getNonSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
832+
private FactoryBean<?> getNonSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
833833
if (isPrototypeCurrentlyInCreation(beanName)) {
834834
return null;
835835
}
@@ -972,7 +972,7 @@ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd
972972
}
973973

974974
// Need to determine the constructor...
975-
Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
975+
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
976976
if (ctors != null ||
977977
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
978978
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
@@ -992,14 +992,14 @@ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd
992992
* @throws org.springframework.beans.BeansException in case of errors
993993
* @see org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
994994
*/
995-
protected Constructor[] determineConstructorsFromBeanPostProcessors(Class<?> beanClass, String beanName)
995+
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(Class<?> beanClass, String beanName)
996996
throws BeansException {
997997

998998
if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
999999
for (BeanPostProcessor bp : getBeanPostProcessors()) {
10001000
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
10011001
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
1002-
Constructor[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
1002+
Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
10031003
if (ctors != null) {
10041004
return ctors;
10051005
}
@@ -1070,7 +1070,7 @@ protected BeanWrapper instantiateUsingFactoryMethod(
10701070
* @return BeanWrapper for the new instance
10711071
*/
10721072
protected BeanWrapper autowireConstructor(
1073-
String beanName, RootBeanDefinition mbd, Constructor[] ctors, Object[] explicitArgs) {
1073+
String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {
10741074

10751075
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
10761076
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ abstract class AutowireUtils {
5656
* decreasing number of arguments.
5757
* @param constructors the constructor array to sort
5858
*/
59-
public static void sortConstructors(Constructor[] constructors) {
60-
Arrays.sort(constructors, new Comparator<Constructor>() {
61-
public int compare(Constructor c1, Constructor c2) {
59+
public static void sortConstructors(Constructor<?>[] constructors) {
60+
Arrays.sort(constructors, new Comparator<Constructor<?>>() {
61+
public int compare(Constructor<?> c1, Constructor<?> c2) {
6262
boolean p1 = Modifier.isPublic(c1.getModifiers());
6363
boolean p2 = Modifier.isPublic(c2.getModifiers());
6464
if (p1 != p2) {
@@ -110,7 +110,7 @@ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
110110
}
111111
// It was declared by CGLIB, but we might still want to autowire it
112112
// if it was actually declared by the superclass.
113-
Class superclass = wm.getDeclaringClass().getSuperclass();
113+
Class<?> superclass = wm.getDeclaringClass().getSuperclass();
114114
return !ClassUtils.hasMethod(superclass, wm.getName(), wm.getParameterTypes());
115115
}
116116

@@ -121,7 +121,7 @@ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
121121
* @param interfaces the Set of interfaces (Class objects)
122122
* @return whether the setter method is defined by an interface
123123
*/
124-
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class> interfaces) {
124+
public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
125125
Method setter = pd.getWriteMethod();
126126
if (setter != null) {
127127
Class<?> targetClass = setter.getDeclaringClass();
@@ -144,10 +144,10 @@ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Clas
144144
*/
145145
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
146146
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
147-
ObjectFactory factory = (ObjectFactory) autowiringValue;
147+
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
148148
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
149149
autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
150-
new Class[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
150+
new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
151151
}
152152
else {
153153
return factory.getObject();
@@ -281,9 +281,9 @@ else if (arg instanceof TypedStringValue) {
281281
@SuppressWarnings("serial")
282282
private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
283283

284-
private final ObjectFactory objectFactory;
284+
private final ObjectFactory<?> objectFactory;
285285

286-
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) {
286+
public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
287287
this.objectFactory = objectFactory;
288288
}
289289

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -84,7 +84,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
8484
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
8585

8686
/** Cache of singleton factories: bean name --> ObjectFactory */
87-
private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
87+
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
8888

8989
/** Cache of early singleton objects: bean name --> bean instance */
9090
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
@@ -181,7 +181,7 @@ protected Object getSingleton(String beanName, boolean allowEarlyReference) {
181181
synchronized (this.singletonObjects) {
182182
singletonObject = this.earlySingletonObjects.get(beanName);
183183
if (singletonObject == null && allowEarlyReference) {
184-
ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
184+
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
185185
if (singletonFactory != null) {
186186
singletonObject = singletonFactory.getObject();
187187
this.earlySingletonObjects.put(beanName, singletonObject);
@@ -201,7 +201,7 @@ protected Object getSingleton(String beanName, boolean allowEarlyReference) {
201201
* with, if necessary
202202
* @return the registered singleton object
203203
*/
204-
public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
204+
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
205205
Assert.notNull(beanName, "'beanName' must not be null");
206206
synchronized (this.singletonObjects) {
207207
Object singletonObject = this.singletonObjects.get(beanName);

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -52,11 +52,11 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
5252
* @return the FactoryBean's object type,
5353
* or {@code null} if the type cannot be determined yet
5454
*/
55-
protected Class getTypeForFactoryBean(final FactoryBean factoryBean) {
55+
protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) {
5656
try {
5757
if (System.getSecurityManager() != null) {
58-
return AccessController.doPrivileged(new PrivilegedAction<Class>() {
59-
public Class run() {
58+
return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
59+
public Class<?> run() {
6060
return factoryBean.getObjectType();
6161
}
6262
}, getAccessControlContext());
@@ -120,7 +120,7 @@ protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName,
120120
* @see org.springframework.beans.factory.FactoryBean#getObject()
121121
*/
122122
private Object doGetObjectFromFactoryBean(
123-
final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
123+
final FactoryBean<?> factory, final String beanName, final boolean shouldPostProcess)
124124
throws BeanCreationException {
125125

126126
Object object;
@@ -190,12 +190,12 @@ protected Object postProcessObjectFromFactoryBean(Object object, String beanName
190190
* @return the bean instance as FactoryBean
191191
* @throws BeansException if the given bean cannot be exposed as a FactoryBean
192192
*/
193-
protected FactoryBean getFactoryBean(String beanName, Object beanInstance) throws BeansException {
193+
protected FactoryBean<?> getFactoryBean(String beanName, Object beanInstance) throws BeansException {
194194
if (!(beanInstance instanceof FactoryBean)) {
195195
throw new BeanCreationException(beanName,
196196
"Bean instance of type [" + beanInstance.getClass() + "] is not a FactoryBean");
197197
}
198-
return (FactoryBean) beanInstance;
198+
return (FactoryBean<?>) beanInstance;
199199
}
200200

201201
/**

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
@@ -55,11 +55,11 @@ protected static FactoryMethods newInstance(TestBean tb, int num, String name) {
5555
return new FactoryMethods(tb, name, num);
5656
}
5757

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

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

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

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

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

1158+
/**
1159+
* Determine the common ancestor of the given classes, if any.
1160+
* @param clazz1 the class to introspect
1161+
* @param clazz2 the other class to introspect
1162+
* @return the common ancestor (i.e. common superclass, one interface
1163+
* extending the other), or {@code null} if none found. If any of the
1164+
* given classes is {@code null}, the other class will be returned.
1165+
* @since 3.2.6
1166+
*/
1167+
public static Class<?> determineCommonAncestor(Class<?> clazz1, Class<?> clazz2) {
1168+
if (clazz1 == null) {
1169+
return clazz2;
1170+
}
1171+
if (clazz2 == null) {
1172+
return clazz1;
1173+
}
1174+
if (clazz1.isAssignableFrom(clazz2)) {
1175+
return clazz1;
1176+
}
1177+
if (clazz2.isAssignableFrom(clazz1)) {
1178+
return clazz2;
1179+
}
1180+
Class<?> ancestor = clazz1;
1181+
do {
1182+
ancestor = ancestor.getSuperclass();
1183+
if (ancestor == null || Object.class.equals(ancestor)) {
1184+
return null;
1185+
}
1186+
}
1187+
while (!ancestor.isAssignableFrom(clazz2));
1188+
return ancestor;
1189+
}
1190+
11581191
/**
11591192
* Check whether the given class is visible in the given ClassLoader.
11601193
* @param clazz the class to check (typically an interface)

0 commit comments

Comments
 (0)